tAdd support for user-level 9P servers/clients and various bug fixes to go with them. - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 32f69c36e0eec1227934bbd34854bfebd88686f2
 (DIR) parent ac244f8d287a6119155ea672c8fd13c487c5e4c7
 (HTM) Author: rsc <devnull@localhost>
       Date:   Thu, 11 Dec 2003 17:48:38 +0000
       
       Add support for user-level 9P servers/clients and various bug fixes to go with them.
       
       Diffstat:
         M bin/B                               |       6 ++++++
         M include/fcall.h                     |       4 ++++
         M include/fs.h                        |       3 ++-
         M include/lib9.h                      |      24 +++++++++++++++++++-----
         M include/thread.h                    |       4 ++--
         M plumb/basic                         |      26 ++++++++++++--------------
         M src/cmd/9p.c                        |      88 +++++++++++++++++++++++++------
         M src/cmd/9pserve.c                   |     457 ++++++++++++++++++++++++++++---
         M src/cmd/9term/9term.c               |       8 +++-----
         M src/cmd/9term/mkfile                |       2 +-
         M src/cmd/dc.c                        |       1 +
         M src/cmd/dict/dict.c                 |      11 +++--------
         M src/cmd/dict/utils.c                |     118 ++++++++++++++++----------------
         M src/cmd/mkfile                      |       4 ++--
         M src/cmd/plumb/fsys.c                |      77 ++++++++++++-------------------
         M src/cmd/plumb/match.c               |      32 ++++++++++++++++----------------
         M src/cmd/plumb/mkfile                |      11 ++++-------
         M src/cmd/plumb/plumber.c             |       9 +++++++--
         M src/cmd/plumb/plumber.h             |       1 +
         M src/cmd/plumb/rules.c               |       4 ++--
         M src/cmd/rc/plan9ish.c               |       5 ++++-
         M src/cmd/sam/unix.c                  |      88 -------------------------------
         M src/cmd/yacc.c                      |      19 +++++--------------
         M src/lib9/announce.c                 |      25 +++++++++++++++++--------
         M src/lib9/await.c                    |       1 +
         M src/lib9/convM2S.c                  |       8 ++++++++
         M src/lib9/convS2M.c                  |      13 +++++++++++++
         M src/lib9/create.c                   |      50 +++++++++++++++++++++++++++++--
         M src/lib9/fcallfmt.c                 |       9 ++++++++-
         M src/lib9/getenv.c                   |      12 ++++++++++++
         M src/lib9/mkfile                     |       9 +++++++++
         M src/lib9/notify.c                   |       2 +-
         M src/lib9/rfork.c                    |       9 ++++++---
         M src/libdraw/openfont.c              |      10 ++--------
         M src/libdraw/x11-alloc.c             |       4 ++--
         M src/libdraw/x11-cload.c             |       2 +-
         M src/libdraw/x11-draw.c              |      10 +++++-----
         M src/libdraw/x11-event.c             |      24 ++++++++++++------------
         M src/libdraw/x11-fill.c              |       6 +++---
         M src/libdraw/x11-get.c               |       6 +++---
         M src/libdraw/x11-init.c              |      10 +++++-----
         M src/libdraw/x11-itrans.c            |      22 +++++++++++-----------
         M src/libdraw/x11-keyboard.c          |       4 ++--
         M src/libdraw/x11-load.c              |       2 +-
         M src/libdraw/x11-memdraw.h           |      32 ++++++++++++++++----------------
         M src/libdraw/x11-mouse.c             |      14 +++++++-------
         M src/libdraw/x11-pixelbits.c         |       2 +-
         M src/libdraw/x11-unload.c            |       2 +-
         M src/libfs/fs.c                      |      39 +++++++++++++++++++------------
         M src/libfs/mkfile                    |       2 ++
         M src/libmux/mux.c                    |       1 -
         M src/libplumb/mesg.c                 |      36 ++++++++-----------------------
         M src/libthread/asm-FreeBSD-386.s     |       4 ++--
         M src/libthread/create.c              |       8 ++++----
         M src/libthread/exec-unix.c           |      22 +++++++++++++++++-----
         M src/libthread/exec.c                |      13 +++++++++----
         M src/libthread/main.c                |       3 ++-
         M src/libthread/note.c                |       4 +---
         M src/libthread/sched.c               |      28 +++++++++++++++++++---------
         M src/libthread/threadimpl.h          |       2 ++
       
       60 files changed, 966 insertions(+), 486 deletions(-)
       ---
 (DIR) diff --git a/bin/B b/bin/B
       t@@ -6,6 +6,12 @@ then
                exit 1
        fi
        
       +for i
       +do
       +        plumb $i
       +done
       +exit 0
       +
        if [ "x$DISPLAY" = "x" ]
        then
                sam="/tmp/.sam.$USER"
 (DIR) diff --git a/include/fcall.h b/include/fcall.h
       t@@ -40,6 +40,7 @@ struct        Fcall
                char        *data;                /* Twrite, Rread */
                ushort        nstat;                /* Twstat, Rstat */
                uchar        *stat;                /* Twstat, Rstat */
       +        int        unixfd;                /* Ropenfd */
        } Fcall;
        
        
       t@@ -100,6 +101,9 @@ enum
                Twstat =        126,
                Rwstat,
                Tmax,
       +
       +        Topenfd =         98,
       +        Ropenfd,
        };
        
        uint        convM2S(uchar*, uint, Fcall*);
 (DIR) diff --git a/include/fs.h b/include/fs.h
       t@@ -12,7 +12,7 @@ typedef struct Fsys Fsys;
        typedef struct Fid Fid;
        
        Fsys *fsinit(int);
       -Fsys *fsmount(int);
       +Fsys *fsmount(int, char*);
        
        int fsversion(Fsys*, int, char*, int);
        Fid *fsauth(Fsys*, char*);
       t@@ -34,6 +34,7 @@ struct Dir *fsdirfstat(Fid*);
        int fsdirwstat(Fsys*, char*, struct Dir*);
        int fsdirfwstat(Fid*, struct Dir*);
        Fid *fsroot(Fsys*);
       +Fsys *nsmount(char*, char*);
        
        #ifdef __cplusplus
        }
 (DIR) diff --git a/include/lib9.h b/include/lib9.h
       t@@ -194,6 +194,7 @@ extern        int        isupperrune(Rune);
         *
        extern        void*        malloc(ulong);
         */
       +extern        void*        p9malloc(ulong);
        extern        void*        mallocz(ulong, int);
        /*
        extern        void        free(void*);
       t@@ -208,6 +209,9 @@ extern        ulong        getrealloctag(void*);
        /*
        extern        void*        malloctopoolblock(void*);
        */
       +#ifndef NOPLAN9DEFINES
       +#define        malloc        p9malloc
       +#endif
        
        /*
         * print routines (provided by <fmt.h>)
       t@@ -442,6 +446,7 @@ extern        void        exits(char*);
        extern        double        frexp(double, int*);
        extern        ulong        getcallerpc(void*);
        extern        char*        p9getenv(char*);
       +extern        int        p9putenv(char*, char*);
        extern        int        getfields(char*, char**, int, int, char*);
        extern        int        gettokens(char *, char **, int, char *);
        extern        char*        getuser(void);
       t@@ -482,6 +487,7 @@ extern        long        time(long*);
        #define        longjmp                p9longjmp
        #undef  setjmp
        #define setjmp                p9setjmp
       +#define putenv                p9putenv
        #define notejmp                p9notejmp
        #define jmp_buf                p9jmp_buf
        #endif
       t@@ -728,14 +734,14 @@ struct IOchunk
        extern        void        _exits(char*);
        
        extern        void        abort(void);
       -/* extern        int        access(char*, int); <unistd.h> */
       +extern        int        p9access(char*, int);
        extern        long        p9alarm(ulong);
        extern        int        await(char*, int);
        /* extern        int        bind(char*, char*, int); give up */
        /* extern        int        brk(void*); <unistd.h> */
       -/* extern        int        chdir(char*); <unistd.h> */
       +extern        int        p9chdir(char*);
        extern        int        close(int);
       -extern        int        create(char*, int, ulong);
       +extern        int        p9create(char*, int, ulong);
        extern        int        p9dup(int, int);
        extern        int        errstr(char*, uint);
        extern        int        p9exec(char*, char*[]);
       t@@ -752,9 +758,9 @@ extern        int        unmount(char*, char*);
        */
        extern        int        noted(int);
        extern        int        notify(void(*)(void*, char*));
       -/* extern        int        open(char*, int); <unistd.h> */
       +extern        int        p9open(char*, int);
        extern        int        fd2path(int, char*, int);
       -extern        int        pipe(int*);
       +extern        int        p9pipe(int*);
        /* 
         * use defs from <unistd.h>
        extern        long        pread(int, void*, long, vlong);
       t@@ -796,6 +802,10 @@ extern        ulong        rendezvous(ulong, ulong);
        #define wait                p9wait
        #define waitpid                p9waitpid
        #define rfork                p9rfork
       +#define access                p9access
       +#define create                p9create
       +#define open                p9open
       +#define pipe                p9pipe
        #endif
        
        extern        Dir*        dirstat(char*);
       t@@ -810,6 +820,10 @@ extern        long        dirreadall(int, Dir**);
        extern        void        rerrstr(char*, uint);
        extern        char*        sysname(void);
        extern        void        werrstr(char*, ...);
       +extern        char*        getns(void);
       +extern        int        sendfd(int, int);
       +extern        int        recvfd(int);
       +extern        int        post9pservice(int, char*);
        
        /* external names that we don't want to step on */
        #ifndef NOPLAN9DEFINES
 (DIR) diff --git a/include/thread.h b/include/thread.h
       t@@ -79,8 +79,8 @@ int                nbsendul(Channel *c, unsigned long v);
        int                proccreate(void (*f)(void *arg), void *arg, unsigned int stacksize);
        int                procrfork(void (*f)(void *arg), void *arg, unsigned int stacksize, int flag);
        void**                procdata(void);
       -void                procexec(Channel *, char *, char *[]);
       -void                procexecl(Channel *, char *, ...);
       +void                procexec(Channel *, int[3], char *, char *[]);
       +void                procexecl(Channel *, int[3], char *, ...);
        int                recv(Channel *c, void *v);
        void*                recvp(Channel *c);
        unsigned long                recvul(Channel *c);
 (DIR) diff --git a/plumb/basic b/plumb/basic
       t@@ -38,7 +38,8 @@ data matches '[a-zA-Z¡-￿0-9_\-./]+'
        data matches '([a-zA-Z¡-￿0-9_\-./]+)\.(jpe?g|JPE?G|gif|GIF|tiff?|TIFF?|ppm|bit)'
        arg isfile        $0
        plumb to image
       -plumb client page -wi
       +plumb start qiv -t $1
       +# plumb client page -wi
        
        # postscript/pdf/dvi go to page but not over the a plumb port
        # the port is here for reference but is unused
       t@@ -47,7 +48,8 @@ data matches '[a-zA-Z¡-￿0-9_\-./]+'
        data matches '([a-zA-Z¡-￿0-9_\-./]+)\.(ps|PS|eps|EPS|pdf|PDF|dvi|DVI)'
        arg isfile        $0
        plumb to postscript
       -plumb start page -w $file
       +plumb start gv $file
       +# plumb start page -w $file
        
        # existing files, possibly tagged by line number, go to editor
        type is text
       t@@ -56,8 +58,7 @@ arg isfile        $1
        data set        $file
        attr add        addr=$3
        plumb to edit
       -plumb start /usr/local/plan9/bin/B $file:$3
       -# plumb client window $editor
       +plumb client $editor
        
        # .h files are looked up in /usr/include and passed to edit
        type is text
       t@@ -66,8 +67,7 @@ arg isfile        /usr/include/$1
        data set        $file
        attr add        addr=$3
        plumb to edit
       -plumb start /usr/local/plan9/bin/B $file:$3
       -# plumb client window $editor
       +plumb client $editor
        
        # .h files are looked up in /usr/local/include and passed to edit
        type is text
       t@@ -76,8 +76,7 @@ arg isfile        /usr/local/include/$1
        data set        $file
        attr add        addr=$3
        plumb to edit
       -plumb start /usr/local/plan9/bin/B $file:$3
       -# plumb client window $editor
       +plumb client $editor
        
        # .h files are looked up in /usr/local/plan9/include and passed to edit
        type is text
       t@@ -86,8 +85,7 @@ arg isfile        /usr/local/plan9/include/$1
        data set        $file
        attr add        addr=$3
        plumb to edit
       -plumb start /usr/local/plan9/bin/B $file:$3
       -# plumb client window $editor
       +plumb client $editor
        
        # .m files are looked up in /sys/module and passed to edit
        type is text
       t@@ -96,8 +94,7 @@ arg isfile        /sys/module/$1
        data set        $file
        attr add        addr=$3
        plumb to edit
       -plumb start /usr/local/plan9/bin/B $file:$3
       -# plumb client window $editor
       +plumb client window $editor
        
        # faces -> new mail window for message
        type        is        text
       t@@ -113,13 +110,14 @@ plumb start rc -c 'man '$2' '$1' >[2=1] | plumb -i -d edit -a ''action=showdata 
        
        # start rule for images without known suffixes
        dst is image
       +arg isfile $data
        plumb to image
       -plumb client page -wi
       +plumb start qiv -t $data
        
        # start rule for postscript without known suffixes
        dst is postscript
        arg isfile $data
       -plumb start page -w $data
       +plumb start gv $data
        
        type        is        text
        data        matches        'Local (.*)'
 (DIR) diff --git a/src/cmd/9p.c b/src/cmd/9p.c
       t@@ -11,7 +11,9 @@ usage(void)
                fprint(2, "usage: 9p [-a address] cmd args...\n");
                fprint(2, "possible cmds:\n");
                fprint(2, "        read name\n");
       +        fprint(2, "        readfd name\n");
                fprint(2, "        write name\n");
       +        fprint(2, "        writefd name\n");
                fprint(2, "        stat name\n");
        //        fprint(2, "        ls name\n");
                fprint(2, "without -a, name elem/path means /path on server unix!$ns/elem\n");
       t@@ -20,6 +22,8 @@ usage(void)
        
        void xread(int, char**);
        void xwrite(int, char**);
       +void xreadfd(int, char**);
       +void xwritefd(int, char**);
        void xstat(int, char**);
        void xls(int, char**);
        
       t@@ -29,6 +33,8 @@ struct {
        } cmds[] = {
                "read", xread,
                "write", xwrite,
       +        "readfd", xreadfd,
       +        "writefd", xwritefd,
                "stat", xstat,
        //        "ls", xls,
        };
       t@@ -64,7 +70,6 @@ Fsys*
        xparse(char *name, char **path)
        {
                int fd;
       -        char *ns;
                char *p;
                Fsys *fs;
        
       t@@ -75,22 +80,17 @@ xparse(char *name, char **path)
                        else
                                *p++ = 0;
                        *path = p;
       -                if(*name == 0)
       -                        usage();
       -                ns = getenv("ns");
       -                if(ns == nil)
       -                        sysfatal("ns not set");
       -                addr = smprint("unix!%s/%s", ns, name);
       -                if(addr == nil)
       -                        sysfatal("out of memory");
       -        }else
       +                fs = nsmount(name, "");
       +                if(fs == nil)
       +                        sysfatal("mount: %r");
       +        }else{
                        *path = name;
       -
       -        fprint(2, "dial %s...", addr);
       -        if((fd = dial(addr, nil, nil, nil)) < 0)
       -                sysfatal("dial: %r");
       -        if((fs = fsmount(fd)) == nil)
       -                sysfatal("fsmount: %r");
       +                fprint(2, "dial %s...", addr);
       +                if((fd = dial(addr, nil, nil, nil)) < 0)
       +                        sysfatal("dial: %r");
       +                if((fs = fsmount(fd, "")) == nil)
       +                        sysfatal("fsmount: %r");
       +        }
                return fs;
        }
        
       t@@ -120,6 +120,15 @@ xopen(char *name, int mode)
                return fid;
        }
        
       +int
       +xopenfd(char *name, int mode)
       +{
       +        Fsys *fs;
       +
       +        fs = xparse(name, &name);
       +        return fsopenfd(fs, name, mode);
       +}
       +
        void
        xread(int argc, char **argv)
        {
       t@@ -144,6 +153,29 @@ xread(int argc, char **argv)
        }
        
        void
       +xreadfd(int argc, char **argv)
       +{
       +        char buf[1024];
       +        int n;
       +        int fd;
       +
       +        ARGBEGIN{
       +        default:
       +                usage();
       +        }ARGEND
       +
       +        if(argc != 1)
       +                usage();
       +
       +        fd = xopenfd(argv[0], OREAD);
       +        while((n = read(fd, buf, sizeof buf)) > 0)
       +                write(1, buf, n);
       +        if(n < 0)
       +                sysfatal("read error: %r");
       +        exits(0);        
       +}
       +
       +void
        xwrite(int argc, char **argv)
        {
                char buf[1024];
       t@@ -168,6 +200,30 @@ xwrite(int argc, char **argv)
        }
        
        void
       +xwritefd(int argc, char **argv)
       +{
       +        char buf[1024];
       +        int n;
       +        int fd;
       +
       +        ARGBEGIN{
       +        default:
       +                usage();
       +        }ARGEND
       +
       +        if(argc != 1)
       +                usage();
       +
       +        fd = xopenfd(argv[0], OWRITE|OTRUNC);
       +        while((n = read(0, buf, sizeof buf)) > 0)
       +                if(write(fd, buf, n) != n)
       +                        sysfatal("write error: %r");
       +        if(n < 0)
       +                sysfatal("read error: %r");
       +        exits(0);        
       +}
       +
       +void
        xstat(int argc, char **argv)
        {
                Dir *d;
 (DIR) diff --git a/src/cmd/9pserve.c b/src/cmd/9pserve.c
       t@@ -2,6 +2,8 @@
        #include <libc.h>
        #include <fcall.h>
        #include <thread.h>
       +#include <poll.h>
       +#include <errno.h>
        
        enum
        {
       t@@ -38,6 +40,7 @@ struct Msg
                int ref;
                int ctag;
                int tag;
       +        int isopenfd;
                Fcall tx;
                Fcall rx;
                Fid *fid;
       t@@ -52,6 +55,8 @@ struct Msg
        struct Conn
        {
                int fd;
       +        int fdmode;
       +        Fid *fdfid;
                int nmsg;
                int nfid;
                Channel *inc;
       t@@ -89,7 +94,7 @@ void *erealloc(void*, int);
        Queue *qalloc(void);
        int sendq(Queue*, void*);
        void *recvq(Queue*);
       -void selectthread(void*);
       +void pollthread(void*);
        void connthread(void*);
        void connoutthread(void*);
        void listenthread(void*);
       t@@ -100,6 +105,10 @@ int tlisten(char*, char*);
        int taccept(int, char*);
        int iolisten(Ioproc*, char*, char*);
        int ioaccept(Ioproc*, int, char*);
       +int iorecvfd(Ioproc*, int);
       +int iosendfd(Ioproc*, int, int);
       +void mainproc(void*);
       +int ignorepipe(void*, char*);
        
        void
        usage(void)
       t@@ -110,14 +119,13 @@ usage(void)
        }
        
        uchar vbuf[128];
       -
       +extern int _threaddebuglevel;
        void
        threadmain(int argc, char **argv)
        {
                char *file;
       -        int n;
       -        Fcall f;
        
       +        if(verbose) fprint(2, "9pserve running\n");
                ARGBEGIN{
                default:
                        usage();
       t@@ -142,6 +150,20 @@ threadmain(int argc, char **argv)
                if((afd = announce(addr, adir)) < 0)
                        sysfatal("announce %s: %r", addr);
        
       +        proccreate(mainproc, nil, STACK);
       +        threadexits(0);
       +}
       +
       +void
       +mainproc(void *v)
       +{
       +        int n;
       +        Fcall f;
       +        USED(v);
       +
       +        yield();        /* let threadmain exit */
       +
       +        atnotify(ignorepipe, 1);
                fmtinstall('D', dirfmt);
                fmtinstall('M', dirmodefmt);
                fmtinstall('F', fcallfmt);
       t@@ -150,10 +172,6 @@ threadmain(int argc, char **argv)
                outq = qalloc();
                inq = qalloc();
        
       -//        threadcreateidle(selectthread, nil, STACK);
       -        threadcreate(inputthread, nil, STACK);
       -        threadcreate(outputthread, nil, STACK);
       -
                f.type = Tversion;
                f.version = "9P2000";
                f.msize = 8192;
       t@@ -165,7 +183,22 @@ threadmain(int argc, char **argv)
                if(convM2S(vbuf, n, &f) != n)
                        sysfatal("convM2S failure");
                if(verbose > 1) fprint(2, "* -> %F\n", &f);
       +
       +        threadcreate(inputthread, nil, STACK);
       +        threadcreate(outputthread, nil, STACK);
                threadcreate(listenthread, nil, STACK);
       +        threadcreateidle(pollthread, nil, STACK);
       +        threadexits(0);
       +}
       +
       +int
       +ignorepipe(void *v, char *s)
       +{
       +        USED(v);
       +        if(strcmp(s, "sys: write on closed pipe") == 0)
       +                return 1;
       +        fprint(2, "msg: %s\n", s);
       +        return 0;
        }
        
        void
       t@@ -178,10 +211,6 @@ listenthread(void *arg)
                USED(arg);
                for(;;){
                        c = emalloc(sizeof(Conn));
       -                c->inc = chancreate(sizeof(void*), 0);
       -                c->internal = chancreate(sizeof(void*), 0);
       -                c->inq = qalloc();
       -                c->outq = qalloc();
                        c->fd = iolisten(io, adir, c->dir);
                        if(c->fd < 0){
                                if(verbose) fprint(2, "listen: %r\n");
       t@@ -189,13 +218,17 @@ listenthread(void *arg)
                                free(c);
                                return;
                        }
       +                c->inc = chancreate(sizeof(void*), 0);
       +                c->internal = chancreate(sizeof(void*), 0);
       +                c->inq = qalloc();
       +                c->outq = qalloc();
                        if(verbose) fprint(2, "incoming call on %s\n", c->dir);
                        threadcreate(connthread, c, STACK);
                }        
        }
        
        void
       -sendmsg(Msg *m)
       +send9pmsg(Msg *m)
        {
                int n, nn;
        
       t@@ -226,7 +259,7 @@ err(Msg *m, char *ename)
                m->rx.type = Rerror;
                m->rx.ename = ename;
                m->rx.tag = m->tx.tag;
       -        sendmsg(m);
       +        send9pmsg(m);
        }
        
        void
       t@@ -250,7 +283,7 @@ connthread(void *arg)
                c->fd = fd;
                threadcreate(connoutthread, c, STACK);
                while((m = mread9p(io, c->fd)) != nil){
       -                if(verbose > 1) fprint(2, "%s -> %F\n", c->dir, &m->tx);
       +                if(verbose > 1) fprint(2, "fd#%d -> %F\n", c->fd, &m->tx);
                        m->c = c;
                        m->ctag = m->tx.tag;
                        c->nmsg++;
       t@@ -267,13 +300,13 @@ connthread(void *arg)
                                        m->rx.msize = 8192;
                                m->rx.version = "9P2000";
                                m->rx.type = Rversion;
       -                        sendmsg(m);
       +                        send9pmsg(m);
                                continue;
                        case Tflush:
                                if((m->oldm = gethash(c->tag, m->tx.oldtag)) == nil){
                                        m->rx.tag = m->tx.tag;
                                        m->rx.type = Rflush;
       -                                sendmsg(m);
       +                                send9pmsg(m);
                                        continue;
                                }
                                m->oldm->ref++;
       t@@ -318,6 +351,15 @@ connthread(void *arg)
                                }
                                m->afid->ref++;
                                break;
       +                case Topenfd:
       +                        if(m->tx.mode != OREAD && (m->tx.mode&~OTRUNC) != OWRITE){
       +                                err(m, "openfd mode must be OREAD or OWRITE");
       +                                continue;
       +                        }
       +                        m->isopenfd = 1;
       +                        m->tx.type = Topen;
       +                        m->tpkt[4] = Topen;
       +                        /* fall through */
                        case Tcreate:
                        case Topen:
                        case Tclunk:
       t@@ -363,6 +405,7 @@ connthread(void *arg)
                                m = msgnew();
                                m->internal = 1;
                                m->c = c;
       +                        c->nmsg++;
                                m->tx.type = Tflush;
                                m->tx.tag = m->tag;
                                m->tx.oldtag = om->tag;
       t@@ -371,7 +414,9 @@ connthread(void *arg)
                                m->ref++;        /* for outq */
                                sendomsg(m);
                                recvp(c->internal);
       -                        msgput(m);
       +                        msgput(m);        /* got from recvp */
       +                        msgput(m);        /* got from msgnew */
       +                        msgput(om);        /* got from hash table */
                        }
                }
        
       t@@ -382,6 +427,7 @@ connthread(void *arg)
                                m = msgnew();
                                m->internal = 1;
                                m->c = c;
       +                        c->nmsg++;
                                m->tx.type = Tclunk;
                                m->tx.tag = m->tag;
                                m->tx.fid = f->fid;
       t@@ -390,7 +436,9 @@ connthread(void *arg)
                                m->ref++;
                                sendomsg(m);
                                recvp(c->internal);
       -                        msgput(m);
       +                        msgput(m);        /* got from recvp */
       +                        msgput(m);        /* got from msgnew */
       +                        fidput(f);        /* got from hash table */
                        }
                }
        
       t@@ -398,7 +446,155 @@ out:
                assert(c->nmsg == 0);
                assert(c->nfid == 0);
                close(c->fd);
       +        chanfree(c->internal);
       +        c->internal = 0;
       +        chanfree(c->inc);
       +        c->inc = 0;
       +        free(c->inq);
       +        c->inq = 0;
       +        free(c->outq);
       +        c->outq = 0;
       +        free(c);
       +}
       +
       +static void
       +openfdthread(void *v)
       +{
       +        Conn *c;
       +        Fid *fid;
       +        Msg *m;
       +        int n;
       +        vlong tot;
       +        Ioproc *io;
       +        char buf[1024];
       +
       +        c = v;
       +        fid = c->fdfid;
       +        io = ioproc();
       +
       +        tot = 0;
       +        if(c->fdmode == OREAD){
       +                for(;;){
       +                        if(verbose) fprint(2, "tread...");
       +                        m = msgnew();
       +                        m->internal = 1;
       +                        m->c = c;
       +                        m->tx.type = Tread;
       +                        m->tx.count = 8192;
       +                        m->tx.fid = fid->fid;
       +                        m->tx.tag = m->tag;
       +                        m->tx.offset = tot;
       +                        m->fid = fid;
       +                        fid->ref++;
       +                        m->ref++;
       +                        sendomsg(m);
       +                        recvp(c->internal);
       +                        if(m->rx.type == Rerror)
       +                                break;
       +                        if(m->rx.count == 0)
       +                                break;
       +                        tot += m->rx.count;
       +                        if(iowrite(io, c->fd, m->rx.data, m->rx.count) != m->rx.count)
       +                                break;
       +                        msgput(m);
       +                        msgput(m);
       +                }
       +        }else{
       +                for(;;){
       +                        if(verbose) fprint(2, "twrite...");
       +                        if((n=ioread(io, c->fd, buf, sizeof buf)) <= 0){
       +                                m = nil;
       +                                break;
       +                        }
       +                        m = msgnew();
       +                        m->internal = 1;
       +                        m->c = c;
       +                        m->tx.type = Twrite;
       +                        m->tx.fid = fid->fid;
       +                        m->tx.data = buf;
       +                        m->tx.count = n;
       +                        m->tx.tag = m->tag;
       +                        m->tx.offset = tot;
       +                        m->fid = fid;
       +                        fid->ref++;
       +                        m->ref++;
       +                        sendomsg(m);
       +                        recvp(c->internal);
       +                        if(m->rx.type == Rerror)
       +                                break;
       +                        tot = n;
       +                        msgput(m);
       +                        msgput(m);
       +                }
       +        }
       +        if(verbose) fprint(2, "eof on %d fid %d\n", c->fd, fid->fid);
       +        close(c->fd);
       +        closeioproc(io);
       +        if(m){
       +                msgput(m);
       +                msgput(m);
       +        }
       +        m = msgnew();
       +        m->internal = 1;
       +        m->c = c;
       +        m->tx.type = Tclunk;
       +        m->tx.fid = fid->fid;
       +        m->fid = fid;
       +        fid->ref++;
       +        m->ref++;
       +        sendomsg(m);
       +        recvp(c->internal);
       +        msgput(m);
       +        msgput(m);
       +        fidput(fid);
       +        c->fdfid = nil;
       +        chanfree(c->internal);
       +        c->internal = 0;
                free(c);
       +}                        
       +
       +int
       +xopenfd(Msg *m)
       +{
       +        char errs[ERRMAX];
       +        int n, p[2];
       +        Conn *nc;
       +
       +        if(pipe(p) < 0){
       +                rerrstr(errs, sizeof errs);
       +                err(m, errs);
       +        }
       +        if(verbose) fprint(2, "xopen pipe %d %d...", p[0], p[1]);
       +
       +        /* now we're committed. */
       +
       +        /* a new connection for this fid */
       +        nc = emalloc(sizeof(Conn));
       +        nc->internal = chancreate(sizeof(void*), 0);
       +
       +        /* a ref for us */
       +        nc->fdfid = m->fid;
       +        m->fid->ref++;
       +        nc->fdmode = m->tx.mode;
       +        nc->fd = p[0];
       +
       +        /* clunk fid from other connection */
       +        if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
       +                fidput(m->fid);
       +
       +        /* a thread to tend the pipe */
       +        threadcreate(openfdthread, nc, STACK);
       +
       +        /* rewrite as Ropenfd */
       +        m->rx.type = Ropenfd;
       +        n = GBIT32(m->rpkt);
       +        m->rpkt = erealloc(m->rpkt, n+4);
       +        PBIT32(m->rpkt+n, p[1]);
       +        n += 4;
       +        PBIT32(m->rpkt, n);
       +        m->rpkt[4] = Ropenfd;
       +        m->rx.unixfd = p[1];
       +        return 0;
        }
        
        void
       t@@ -413,6 +609,9 @@ connoutthread(void *arg)
                io = ioproc();
                while((m = recvq(c->outq)) != nil){
                        err = m->tx.type+1 != m->rx.type;
       +                if(!err && m->isopenfd)
       +                        if(xopenfd(m) < 0)
       +                                continue;
                        switch(m->tx.type){
                        case Tflush:
                                om = m->oldm;
       t@@ -446,7 +645,7 @@ connoutthread(void *arg)
                        }
                        if(delhash(m->c->tag, m->ctag, m) == 0)
                                msgput(m);
       -                if(verbose > 1) fprint(2, "%s <- %F\n", c->dir, &m->rx);
       +                if(verbose > 1) fprint(2, "fd#%d <- %F\n", c->fd, &m->rx);
                        rewritehdr(&m->rx, m->rpkt);
                        if(mwrite9p(io, c->fd, m->rpkt) < 0)
                                if(verbose) fprint(2, "write error: %r\n");
       t@@ -473,6 +672,8 @@ outputthread(void *arg)
                        msgput(m);
                }
                closeioproc(io);
       +        fprint(2, "output eof\n");
       +        threadexitsall(0);
        }        
        
        void
       t@@ -483,6 +684,7 @@ inputthread(void *arg)
                Msg *m;
                Ioproc *io;
        
       +        if(verbose) fprint(2, "input thread\n");
                io = ioproc();
                USED(arg);
                while((pkt = read9ppkt(io, 0)) != nil){
       t@@ -514,6 +716,8 @@ inputthread(void *arg)
                                sendq(m->c->outq, m);
                }
                closeioproc(io);
       +        fprint(2, "input eof\n");
       +        threadexitsall(0);
        }
        
        void*
       t@@ -626,15 +830,20 @@ msgput(Msg *m)
                m->c->nmsg--;
                m->c = nil;
                fidput(m->fid);
       -        fidput(m->afid);
       -        fidput(m->newfid);
       -        free(m->tpkt);
       -        free(m->rpkt);
                m->fid = nil;
       +        fidput(m->afid);
                m->afid = nil;
       +        fidput(m->newfid);
                m->newfid = nil;
       +        free(m->tpkt);
                m->tpkt = nil;
       +        free(m->rpkt);
                m->rpkt = nil;
       +        if(m->rx.type == Ropenfd)
       +                close(m->rx.unixfd);
       +        m->rx.unixfd = -1;
       +        m->isopenfd = 0;
       +        m->internal = 0;
                m->next = freemsg;
                freemsg = m;
        }
       t@@ -649,6 +858,7 @@ msgget(int n)
                m = msgtab[n];
                if(m->ref == 0)
                        return nil;
       +        if(verbose) fprint(2, "msgget %d = %p\n", n, m);
                m->ref++;
                return m;
        }
       t@@ -768,6 +978,12 @@ read9ppkt(Ioproc *io, int fd)
                        free(pkt);
                        return nil;
                }
       +/* would do this if we ever got one of these, but we only generate them
       +        if(pkt[4] == Ropenfd){
       +                newfd = iorecvfd(io, fd);
       +                PBIT32(pkt+n-4, newfd);
       +        }
       +*/
                return pkt;
        }
        
       t@@ -795,7 +1011,7 @@ mread9p(Ioproc *io, int fd)
        int
        mwrite9p(Ioproc *io, int fd, uchar *pkt)
        {
       -        int n;
       +        int n, nfd;
        
                n = GBIT32(pkt);
                if(verbose > 2) fprint(2, "write %d %d %.*H\n", fd, n, n, pkt);
       t@@ -803,6 +1019,13 @@ mwrite9p(Ioproc *io, int fd, uchar *pkt)
                        fprint(2, "write error: %r\n");
                        return -1;
                }
       +        if(pkt[4] == Ropenfd){
       +                nfd = GBIT32(pkt+n-4);
       +                if(iosendfd(io, fd, nfd) < 0){
       +                        fprint(2, "send fd error: %r\n");
       +                        return -1;
       +                }
       +        }
                return 0;
        }
        
       t@@ -871,42 +1094,212 @@ rewritehdr(Fcall *f, uchar *pkt)
        
        #ifdef _LIB9_H_
        /* unix select-based polling */
       +struct Ioproc
       +{
       +        Channel *c;
       +        Ioproc *next;
       +        int index;
       +};
       +
       +static struct Ioproc **pio;
       +static struct pollfd *pfd;
       +static int npfd;
       +static struct Ioproc *iofree;
       +
        Ioproc*
        ioproc(void)
        {
       -        return nil;
       +        Ioproc *io;
       +
       +        if(iofree == nil){
       +                pfd = erealloc(pfd, (npfd+1)*sizeof(pfd[0]));
       +                pfd[npfd].events = 0;
       +                pfd[npfd].fd = -1;
       +                iofree = emalloc(sizeof(Ioproc));
       +                iofree->index = npfd;
       +                iofree->c = chancreate(sizeof(ulong), 1);
       +                pio = erealloc(pio, (npfd+1)*sizeof(pio[0]));
       +                pio[npfd] = iofree;
       +                npfd++;
       +        }
       +        io = iofree;
       +        iofree = io->next;
       +        return io;
       +}
       +
       +void
       +closeioproc(Ioproc *io)
       +{
       +        io->next = iofree;
       +        iofree = io;
       +}
       +
       +void
       +pollthread(void *v)
       +{
       +        int i, n;
       +
       +        for(;;){
       +                yield();
       +                for(i=0; i<npfd; i++)
       +                        pfd[i].revents = 0;
       +                if(verbose){
       +                        fprint(2, "poll:");
       +                        for(i=0; i<npfd; i++)
       +                                if(pfd[i].events)
       +                                        fprint(2, " %d%c", pfd[i].fd, pfd[i].events==POLLIN ? 'r' : pfd[i].events==POLLOUT ? 'w' : '?');
       +                        fprint(2, "\n");
       +                }
       +                n = poll(pfd, npfd, -1);
       +                if(n <= 0)
       +                        continue;
       +                for(i=0; i<npfd; i++)
       +                        if(pfd[i].fd != -1 && pfd[i].revents){
       +                                pfd[i].fd = -1;
       +                                pfd[i].events = 0;
       +                                pfd[i].revents = 0;
       +                                nbsendul(pio[i]->c, 1);
       +                        }
       +        }        
       +}
       +
       +static void
       +noblock(int fd)
       +{
       +        fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0)|O_NONBLOCK);
       +}
       +
       +static void
       +xwait(Ioproc *io, int fd, int e)
       +{
       +        if(verbose) fprint(2, "wait for %d%c\n", fd, e==POLLIN ? 'r' : 'w');
       +        pfd[io->index].fd = fd;
       +        pfd[io->index].events = e;
       +        recvul(io->c);
       +        if(verbose) fprint(2, "got %d\n", fd);
       +}
       +
       +static void
       +rwait(Ioproc *io, int fd)
       +{
       +        xwait(io, fd, POLLIN);
       +}
       +
       +static void
       +wwait(Ioproc *io, int fd)
       +{
       +        xwait(io, fd, POLLOUT);
        }
        
        long
        ioread(Ioproc *io, int fd, void *v, long n)
        {
       +        long r;
                USED(io);
        
       -        xxx;
       +        noblock(fd);
       +        while((r=read(fd, v, n)) < 0 && errno == EWOULDBLOCK)
       +                rwait(io, fd);
       +        return r;
        }
        
        long
       -iowrite(Ioproc *io, int fd, void *v, long n)
       +ioreadn(Ioproc *io, int fd, void *v, long n)
       +{
       +        long tot, m;
       +        uchar *u;
       +
       +        u = v;
       +        for(tot=0; tot<n; tot+=m){
       +                m = ioread(io, fd, u+tot, n-tot);
       +                if(m <= 0){
       +                        if(tot)
       +                                break;
       +                        return m;
       +                }
       +        }
       +        return tot;
       +}
       +
       +int
       +iorecvfd(Ioproc *io, int fd)
        {
       +        int r;
       +
       +        noblock(fd);
       +        while((r=recvfd(fd)) < 0 && errno == EWOULDBLOCK)
       +                rwait(io, fd);
       +        return r;
       +}
       +
       +int
       +iosendfd(Ioproc *io, int s, int fd)
       +{
       +        int r;
       +
       +        noblock(s);
       +        while((r=sendfd(s, fd)) < 0 && errno == EWOULDBLOCK)
       +                wwait(io, s);
       +if(r < 0) fprint(2, "sent %d, %d\n", s, fd);
       +        return r;
       +}
       +
       +static long
       +_iowrite(Ioproc *io, int fd, void *v, long n)
       +{
       +        long r;
                USED(io);
        
       -        xxx;
       +        noblock(fd);
       +        while((r=write(fd, v, n)) < 0 && errno == EWOULDBLOCK)
       +                wwait(io, fd);
       +        return r;
       +}
       +
       +long
       +iowrite(Ioproc *io, int fd, void *v, long n)
       +{
       +        long tot, m;
       +        uchar *u;
       +
       +        u = v;
       +        for(tot=0; tot<n; tot+=m){
       +                m = _iowrite(io, fd, u+tot, n-tot);
       +                if(m <= 0){
       +                        if(tot)
       +                                break;
       +                        return m;
       +                }
       +        }
       +        return tot;
        }
        
        int
       -iolisten(Ioproc *io, char *a, char *b)
       +iolisten(Ioproc *io, char *dir, char *ndir)
        {
       +        int fd;
       +        int r;
       +        extern int _p9netfd(char*);
                USED(io);
        
       -        xxx;
       +        if((fd = _p9netfd(dir)) < 0)
       +                return -1;
       +        noblock(fd);
       +        while((r=listen(dir, ndir)) < 0 && errno == EWOULDBLOCK)
       +                rwait(io, fd);
       +        return r;
        }
        
        int
        ioaccept(Ioproc *io, int fd, char *dir)
        {
       +        int r;
                USED(io);
        
       -        xxx;
       +        noblock(fd);
       +        while((r=accept(fd, dir)) < 0 && errno == EWOULDBLOCK)
       +                rwait(io, fd);
       +        return r;
        }
        
        #else
 (DIR) diff --git a/src/cmd/9term/9term.c b/src/cmd/9term/9term.c
       t@@ -1212,7 +1212,6 @@ rcstart(int fd[2], int argc, char **argv)
                        argv[1] = "-i";
                        argv[2] = 0;
                }
       -
                /*
                 * fd0 is slave (tty), fd1 is master (pty)
                 */
       t@@ -1222,7 +1221,7 @@ rcstart(int fd[2], int argc, char **argv)
        
                switch(pid = fork()) {
                case 0:
       -                putenv("TERM=9term");
       +                putenv("TERM", "9term");
                        close(fd[1]);
                        setsid();
                //        tcsetpgrp(0, pid);
       t@@ -1238,6 +1237,7 @@ rcstart(int fd[2], int argc, char **argv)
                        dup(sfd, 2);
                        system("stty tabs -onlcr -echo");
                        execvp(argv[0], argv);
       +                fprint(2, "exec %s failed: %r\n", argv[0]);
                        _exits("oops");
                        break;
                case -1:
       t@@ -1388,9 +1388,7 @@ scroll(int but)
        void
        plumbstart(void)
        {
       -        char buf[256];
       -        snprint(buf, sizeof buf,  "%s/mnt/plumb", getenv("HOME"));
       -        if((plumbfd = plumbopen(buf, OWRITE)) < 0)
       +        if((plumbfd = plumbopen("send", OWRITE)) < 0)
                        fatal("plumbopen");
        }
        
 (DIR) diff --git a/src/cmd/9term/mkfile b/src/cmd/9term/mkfile
       t@@ -9,5 +9,5 @@ OFILES=\
        
        <$PLAN9/src/mkone
        
       -LDFLAGS=-lframe -ldraw -lplumb -lthread -l9 -lfmt -lutf -L$X11/lib -lX11 -lutil
       +LDFLAGS=-lframe -ldraw -lplumb -lfs -lmux -lthread -l9 -lfmt -lutf -L$X11/lib -lX11 -lutil
        
 (DIR) diff --git a/src/cmd/dc.c b/src/cmd/dc.c
       t@@ -21,6 +21,7 @@ typedef        void*        pointer;
        #define NE 3
        #define length(p)        ((p)->wt-(p)->beg)
        #define rewind(p)        (p)->rd=(p)->beg
       +#undef create
        #define create(p)        (p)->rd = (p)->wt = (p)->beg
        #define fsfile(p)        (p)->rd = (p)->wt
        #define truncate(p)        (p)->wt = (p)->rd
 (DIR) diff --git a/src/cmd/dict/dict.c b/src/cmd/dict/dict.c
       t@@ -59,18 +59,13 @@ void
        main(int argc, char **argv)
        {
                int i, cmd, kflag;
       -        char *line, *p, *root;
       +        char *line, *p;
        
                Binit(&binbuf, 0, OREAD);
                Binit(&boutbuf, 1, OWRITE);
                kflag = 0;
                line = 0;
                dict = 0;
       -        root = getenv("PLAN9");
       -        if(root == nil)
       -                root = "/usr/local/plan9";
       -        if(chdir(root) < 0)
       -                sysfatal("chdir %s: %r", root);
        
                for(i=0; dicts[i].name; i++){
                        if(access(dicts[i].path, 0)>=0 && access(dicts[i].indexpath, 0)>=0){
       t@@ -126,12 +121,12 @@ main(int argc, char **argv)
                }
                bdict = Bopen(dict->path, OREAD);
                if(!bdict) {
       -                err("can't open dictionary %s/%s", root, dict->path);
       +                err("can't open dictionary %s", dict->path);
                        exits("nodict");
                }
                bindex = Bopen(dict->indexpath, OREAD);
                if(!bindex) {
       -                err("can't open index %s/%s", root, dict->indexpath);
       +                err("can't open index %s", dict->indexpath);
                        exits("noindex");
                }
                indextop = Bseek(bindex, 0L, 2);
 (DIR) diff --git a/src/cmd/dict/utils.c b/src/cmd/dict/utils.c
       t@@ -5,160 +5,160 @@
        
        Dict dicts[] = {
                {"oed",                "Oxford English Dictionary, 2nd Ed.",
       -         "dict/oed2",        "dict/oed2index",
       +         "#9/dict/oed2",        "#9/dict/oed2index",
                 oednextoff,        oedprintentry,                oedprintkey},
                {"ahd",                "American Heritage Dictionary, 2nd College Ed.",
                 "ahd/DICT.DB",        "ahd/index",
                 ahdnextoff,        ahdprintentry,                ahdprintkey},
                {"pgw",                "Project Gutenberg Webster Dictionary",
       -         "dict/pgw",        "dict/pgwindex",
       +         "#9/dict/pgw",        "#9/dict/pgwindex",
                 pgwnextoff,        pgwprintentry,                pgwprintkey},
                {"thesaurus",        "Collins Thesaurus",
       -         "dict/thesaurus",        "dict/thesindex",
       +         "#9/dict/thesaurus",        "#9/dict/thesindex",
                 thesnextoff,        thesprintentry,        thesprintkey},
        
                {"ce",                "Gendai Chinese->English",
       -         "dict/world/sansdata/sandic24.dat",
       -         "dict/world/sansdata/ceindex",
       +         "#9/dict/world/sansdata/sandic24.dat",
       +         "#9/dict/world/sansdata/ceindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
                {"ceh",                "Gendai Chinese->English (Hanzi index)",
       -         "dict/world/sansdata/sandic24.dat",
       -         "dict/world/sansdata/cehindex",
       +         "#9/dict/world/sansdata/sandic24.dat",
       +         "#9/dict/world/sansdata/cehindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
                {"ec",                "Gendai English->Chinese",
       -         "dict/world/sansdata/sandic24.dat",
       -         "dict/world/sansdata/ecindex",
       +         "#9/dict/world/sansdata/sandic24.dat",
       +         "#9/dict/world/sansdata/ecindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
        
                {"dae",                "Gyldendal Danish->English",
       -         "dict/world/gylddata/sandic30.dat",
       -         "dict/world/gylddata/daeindex",
       +         "#9/dict/world/gylddata/sandic30.dat",
       +         "#9/dict/world/gylddata/daeindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
                {"eda",                "Gyldendal English->Danish",
       -         "dict/world/gylddata/sandic29.dat",
       -         "dict/world/gylddata/edaindex",
       +         "#9/dict/world/gylddata/sandic29.dat",
       +         "#9/dict/world/gylddata/edaindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
        
                {"due",                "Wolters-Noordhoff Dutch->English",
       -         "dict/world/woltdata/sandic07.dat",
       -         "dict/world/woltdata/deindex",
       +         "#9/dict/world/woltdata/sandic07.dat",
       +         "#9/dict/world/woltdata/deindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
                {"edu",                "Wolters-Noordhoff English->Dutch",
       -         "dict/world/woltdata/sandic06.dat",
       -         "dict/world/woltdata/edindex",
       +         "#9/dict/world/woltdata/sandic06.dat",
       +         "#9/dict/world/woltdata/edindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
        
                {"fie",                "WSOY Finnish->English",
       -         "dict/world/werndata/sandic32.dat",
       -         "dict/world/werndata/fieindex",
       +         "#9/dict/world/werndata/sandic32.dat",
       +         "#9/dict/world/werndata/fieindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
                {"efi",                "WSOY English->Finnish",
       -         "dict/world/werndata/sandic31.dat",
       -         "dict/world/werndata/efiindex",
       +         "#9/dict/world/werndata/sandic31.dat",
       +         "#9/dict/world/werndata/efiindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
        
                {"fe",                "Collins French->English",
       -         "dict/fe",        "dict/feindex",
       +         "#9/dict/fe",        "#9/dict/feindex",
                 pcollnextoff,        pcollprintentry,        pcollprintkey},
                {"ef",                "Collins English->French",
       -         "dict/ef",        "dict/efindex",
       +         "#9/dict/ef",        "#9/dict/efindex",
                 pcollnextoff,        pcollprintentry,        pcollprintkey},
        
                {"ge",                "Collins German->English",
       -         "dict/ge",        "dict/geindex",
       +         "#9/dict/ge",        "#9/dict/geindex",
                 pcollgnextoff,        pcollgprintentry,        pcollgprintkey},
                {"eg",                "Collins English->German",
       -         "dict/eg",        "dict/egindex",
       +         "#9/dict/eg",        "#9/dict/egindex",
                 pcollgnextoff,        pcollgprintentry,        pcollgprintkey},
        
                {"ie",                "Collins Italian->English",
       -         "dict/ie",        "dict/ieindex",
       +         "#9/dict/ie",        "#9/dict/ieindex",
                 pcollnextoff,        pcollprintentry,        pcollprintkey},
                {"ei",                "Collins English->Italian",
       -         "dict/ei",        "dict/eiindex",
       +         "#9/dict/ei",        "#9/dict/eiindex",
                 pcollnextoff,        pcollprintentry,        pcollprintkey},
        
                {"je",                "Sanshusha Japanese->English",
       -         "dict/world/sansdata/sandic18.dat",
       -         "dict/world/sansdata/jeindex",
       +         "#9/dict/world/sansdata/sandic18.dat",
       +         "#9/dict/world/sansdata/jeindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
                {"jek",                "Sanshusha Japanese->English (Kanji index)",
       -         "dict/world/sansdata/sandic18.dat",
       -         "dict/world/sansdata/jekindex",
       +         "#9/dict/world/sansdata/sandic18.dat",
       +         "#9/dict/world/sansdata/jekindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
                {"ej",                "Sanshusha English->Japanese",
       -         "dict/world/sansdata/sandic18.dat",
       -         "dict/world/sansdata/ejindex",
       +         "#9/dict/world/sansdata/sandic18.dat",
       +         "#9/dict/world/sansdata/ejindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
        
                {"tjeg",        "Sanshusha technical Japanese->English,German",
       -         "dict/world/sansdata/sandic16.dat",
       -         "dict/world/sansdata/tjegindex",
       +         "#9/dict/world/sansdata/sandic16.dat",
       +         "#9/dict/world/sansdata/tjegindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
                {"tjegk",        "Sanshusha technical Japanese->English,German (Kanji index)",
       -         "dict/world/sansdata/sandic16.dat",
       -         "dict/world/sansdata/tjegkindex",
       +         "#9/dict/world/sansdata/sandic16.dat",
       +         "#9/dict/world/sansdata/tjegkindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
                {"tegj",        "Sanshusha technical English->German,Japanese",
       -         "dict/world/sansdata/sandic16.dat",
       -         "dict/world/sansdata/tegjindex",
       +         "#9/dict/world/sansdata/sandic16.dat",
       +         "#9/dict/world/sansdata/tegjindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
                {"tgje",        "Sanshusha technical German->Japanese,English",
       -         "dict/world/sansdata/sandic16.dat",
       -         "dict/world/sansdata/tgjeindex",
       +         "#9/dict/world/sansdata/sandic16.dat",
       +         "#9/dict/world/sansdata/tgjeindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
        
                {"ne",                "Kunnskapforlaget Norwegian->English",
       -         "dict/world/kunndata/sandic28.dat",
       -         "dict/world/kunndata/neindex",
       +         "#9/dict/world/kunndata/sandic28.dat",
       +         "#9/dict/world/kunndata/neindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
                {"en",                "Kunnskapforlaget English->Norwegian",
       -         "dict/world/kunndata/sandic27.dat",
       -         "dict/world/kunndata/enindex",
       +         "#9/dict/world/kunndata/sandic27.dat",
       +         "#9/dict/world/kunndata/enindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
        
                {"re",                "Leon Ungier Russian->English",
       -         "dict/re",        "dict/reindex",
       +         "#9/dict/re",        "#9/dict/reindex",
                 simplenextoff,        simpleprintentry,        simpleprintkey},
                {"er",                "Leon Ungier English->Russian",
       -         "dict/re",        "dict/erindex",
       +         "#9/dict/re",        "#9/dict/erindex",
                 simplenextoff,        simpleprintentry,        simpleprintkey},
        
                {"se",                "Collins Spanish->English",
       -         "dict/se",        "dict/seindex",
       +         "#9/dict/se",        "#9/dict/seindex",
                 pcollnextoff,        pcollprintentry,        pcollprintkey},
                {"es",                "Collins English->Spanish",
       -         "dict/es",        "dict/esindex",
       +         "#9/dict/es",        "#9/dict/esindex",
                 pcollnextoff,        pcollprintentry,        pcollprintkey},
        
                {"swe",                "Esselte Studium Swedish->English",
       -         "dict/world/essedata/sandic34.dat",
       -         "dict/world/essedata/sweindex",
       +         "#9/dict/world/essedata/sandic34.dat",
       +         "#9/dict/world/essedata/sweindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
                {"esw",                "Esselte Studium English->Swedish",
       -         "dict/world/essedata/sandic33.dat",
       -         "dict/world/essedata/eswindex",
       +         "#9/dict/world/essedata/sandic33.dat",
       +         "#9/dict/world/essedata/eswindex",
                 worldnextoff,        worldprintentry,        worldprintkey},
        
                {"movie",        "Movies -- by title",
       -         "movie/data",        "dict/movtindex",
       +         "movie/data",        "#9/dict/movtindex",
                 movienextoff,        movieprintentry,        movieprintkey},
                {"moviea",        "Movies -- by actor",
       -         "movie/data",        "dict/movaindex",
       +         "movie/data",        "#9/dict/movaindex",
                 movienextoff,        movieprintentry,        movieprintkey},
                {"movied",        "Movies -- by director",
       -         "movie/data",        "dict/movdindex",
       +         "movie/data",        "#9/dict/movdindex",
                 movienextoff,        movieprintentry,        movieprintkey},
        
                {"slang",        "English Slang",
       -         "dict/slang",        "dict/slangindex",
       +         "#9/dict/slang",        "#9/dict/slangindex",
                 slangnextoff,        slangprintentry,        slangprintkey},
        
                {"robert",        "Robert Électronique",
       -         "dict/robert/_pointers",        "dict/robert/_index",
       +         "#9/dict/robert/_pointers",        "#9/dict/robert/_index",
                 robertnextoff,        robertindexentry,        robertprintkey},
                {"robertv",        "Robert Électronique - formes des verbes",
       -         "dict/robert/flex.rob",        "dict/robert/_flexindex",
       +         "#9/dict/robert/flex.rob",        "#9/dict/robert/_flexindex",
                 robertnextflex,        robertflexentry,        robertprintkey},
        
                {0, 0, 0, 0, 0}
 (DIR) diff --git a/src/cmd/mkfile b/src/cmd/mkfile
       t@@ -2,11 +2,11 @@ PLAN9=../..
        <$PLAN9/src/mkhdr
        
        TARG=`ls *.c | sed 's/\.c//'`
       -LDFLAGS=$LDFLAGS -lthread -lsec -lfs -lmux -lregexp9 -l9 -lbio -lfmt -lutf
       +LDFLAGS=$LDFLAGS -lthread -lsec -lfs -lmux -lregexp9 -lbio -l9 -lfmt -lutf
        
        <$PLAN9/src/mkmany
        
       -BUGGERED='CVS|oplumb|plumb|plumb2|mk|vac|9term|venti|htmlfmt'
       +BUGGERED='CVS|oplumb|plumb2|mk|vac|9term|venti|htmlfmt'
        DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "$BUGGERED"`
        
        <$PLAN9/src/mkdirs
 (DIR) diff --git a/src/cmd/plumb/fsys.c b/src/cmd/plumb/fsys.c
       t@@ -3,14 +3,13 @@
        #include <bio.h>
        #include <regexp.h>
        #include <thread.h>
       -#include <auth.h>
        #include <fcall.h>
        #include <plumb.h>
        #include "plumber.h"
        
        enum
        {
       -        Stack = 8*1024
       +        Stack = 32*1024
        };
        
        typedef struct Dirtab Dirtab;
       t@@ -73,13 +72,12 @@ struct Holdq
        
        struct        /* needed because incref() doesn't return value */
        {
       -        Lock;
       -        int                        ref;
       +        Lock        lk;
       +        int        ref;
        } rulesref;
        
        enum
        {
       -        DEBUG        = 0,
                NDIR        = 50,
                Nhash        = 16,
        
       t@@ -99,13 +97,10 @@ static Dirtab dir[NDIR] =
        static int        ndir = NQID;
        
        static int                srvfd;
       -static int                srvclosefd;                        /* rock for end of pipe to close */
       -static int                clockfd;
        static int                clock;
        static Fid                *fids[Nhash];
        static QLock        readlock;
        static QLock        queue;
       -static char        srvfile[128];
        static int                messagesize = 8192+IOHDRSZ;        /* good start */
        
        static void        fsysproc(void*);
       t@@ -183,54 +178,35 @@ addport(char *port)
        static ulong
        getclock(void)
        {
       -        char buf[32];
       -
       -        seek(clockfd, 0, 0);
       -        read(clockfd, buf, sizeof buf);
       -        return atoi(buf);
       +        return time(0);
        }
        
        void
        startfsys(void)
        {
       -        int p[2], fd;
       +        int p[2];
        
                fmtinstall('F', fcallfmt);
       -        clockfd = open("/dev/time", OREAD|OCEXEC);
                clock = getclock();
                if(pipe(p) < 0)
                        error("can't create pipe: %r");
                /* 0 will be server end, 1 will be client end */
                srvfd = p[0];
       -        srvclosefd = p[1];
       -        sprint(srvfile, "/srv/plumb.%s.%d", user, getpid());
       -        if(putenv("plumbsrv", srvfile) < 0)
       -                error("can't write $plumbsrv: %r");
       -        fd = create(srvfile, OWRITE|OCEXEC|ORCLOSE, 0600);
       -        if(fd < 0)
       -                error("can't create /srv file: %r");
       -        if(fprint(fd, "%d", p[1]) <= 0)
       -                error("can't write /srv/file: %r");
       -        /* leave fd open; ORCLOSE will take care of it */
       -
       -        procrfork(fsysproc, nil, Stack, RFFDG);
       -
       -        close(p[0]);
       -        if(mount(p[1], -1, "/mnt/plumb", MREPL, "") < 0)
       -                error("can't mount /mnt/plumb: %r");
       +        if(post9pservice(p[1], "plumb") < 0)
       +                sysfatal("post9pservice plumb: %r");
                close(p[1]);
       +        proccreate(fsysproc, nil, Stack);
        }
        
        static void
       -fsysproc(void*)
       +fsysproc(void *v)
        {
                int n;
                Fcall *t;
                Fid *f;
                uchar *buf;
        
       -        close(srvclosefd);
       -        srvclosefd = -1;
       +        USED(v);
                t = nil;
                for(;;){
                        buf = malloc(messagesize);        /* avoid memset of emalloc */
       t@@ -250,7 +226,7 @@ fsysproc(void*)
                                t = emalloc(sizeof(Fcall));
                        if(convM2S(buf, n, t) != n)
                                error("convert error in convM2S");
       -                if(DEBUG)
       +                if(debug)
                                fprint(2, "<= %F\n", t);
                        if(fcall[t->type] == nil)
                                fsysrespond(t, buf, Ebadfcall);
       t@@ -281,7 +257,7 @@ fsysrespond(Fcall *t, uchar *buf, char *err)
                        error("convert error in convS2M");
                if(write(srvfd, buf, n) != n)
                        error("write error in respond");
       -        if(DEBUG)
       +        if(debug)
                        fprint(2, "=> %F\n", t);
                free(buf);
        }
       t@@ -555,8 +531,10 @@ dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e)
        }
        
        static Fcall*
       -fsysversion(Fcall *t, uchar *buf, Fid*)
       +fsysversion(Fcall *t, uchar *buf, Fid *fid)
        {
       +        USED(fid);
       +
                if(t->msize < 256){
                        fsysrespond(t, buf, "version: message size too small");
                        return t;
       t@@ -574,8 +552,9 @@ fsysversion(Fcall *t, uchar *buf, Fid*)
        }
        
        static Fcall*
       -fsysauth(Fcall *t, uchar *buf, Fid*)
       +fsysauth(Fcall *t, uchar *buf, Fid *fid)
        {
       +        USED(fid);
                fsysrespond(t, buf, "plumber: authentication not required");
                return t;
        }
       t@@ -605,10 +584,11 @@ fsysattach(Fcall *t, uchar *buf, Fid *f)
        }
        
        static Fcall*
       -fsysflush(Fcall *t, uchar *buf, Fid*)
       +fsysflush(Fcall *t, uchar *buf, Fid *fid)
        {
                int i;
        
       +        USED(fid);
                qlock(&queue);
                for(i=NQID; i<ndir; i++)
                        flushqueue(&dir[i], t->oldtag);
       t@@ -729,14 +709,14 @@ fsysopen(Fcall *t, uchar *buf, Fid *f)
                if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
                        goto Deny;
                if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){
       -                lock(&rulesref);
       +                lock(&rulesref.lk);
                        if(rulesref.ref++ != 0){
                                rulesref.ref--;
       -                        unlock(&rulesref);
       +                        unlock(&rulesref.lk);
                                fsysrespond(t, buf, Einuse);
                                return t;
                        }
       -                unlock(&rulesref);
       +                unlock(&rulesref.lk);
                }
                if(clearrules){
                        writerules(nil, 0);
       t@@ -761,8 +741,9 @@ fsysopen(Fcall *t, uchar *buf, Fid *f)
        }
        
        static Fcall*
       -fsyscreate(Fcall *t, uchar *buf, Fid*)
       +fsyscreate(Fcall *t, uchar *buf, Fid *fid)
        {
       +        USED(fid);
                fsysrespond(t, buf, Eperm);
                return t;
        }
       t@@ -916,15 +897,17 @@ fsysstat(Fcall *t, uchar *buf, Fid *f)
        }
        
        static Fcall*
       -fsyswstat(Fcall *t, uchar *buf, Fid*)
       +fsyswstat(Fcall *t, uchar *buf, Fid *fid)
        {
       +        USED(fid);
                fsysrespond(t, buf, Eperm);
                return t;
        }
        
        static Fcall*
       -fsysremove(Fcall *t, uchar *buf, Fid*)
       +fsysremove(Fcall *t, uchar *buf, Fid *fid)
        {
       +        USED(fid);
                fsysrespond(t, buf, Eperm);
                return t;
        }
       t@@ -945,9 +928,9 @@ fsysclunk(Fcall *t, uchar *buf, Fid *f)
                                 * unless last write ended with a blank line
                                 */
                                writerules(nil, 0);
       -                        lock(&rulesref);
       +                        lock(&rulesref.lk);
                                rulesref.ref--;
       -                        unlock(&rulesref);
       +                        unlock(&rulesref.lk);
                        }
                        prev = nil;
                        for(p=d->fopen; p; p=p->nextopen){
 (DIR) diff --git a/src/cmd/plumb/match.c b/src/cmd/plumb/match.c
       t@@ -6,6 +6,7 @@
        #include <plumb.h>
        #include "plumber.h"
        
       +/*
        static char*
        nonnil(char *s)
        {
       t@@ -13,6 +14,7 @@ nonnil(char *s)
                        return "";
                return s;
        }
       +*/
        
        int
        verbis(int obj, Plumbmsg *m, Rule *r)
       t@@ -44,10 +46,10 @@ setvar(Resub rs[10], char *match[10])
                        free(match[i]);
                        match[i] = nil;
                }
       -        for(i=0; i<10 && rs[i].sp!=nil; i++){
       -                n = rs[i].ep-rs[i].sp;
       +        for(i=0; i<10 && rs[i].s.sp!=nil; i++){
       +                n = rs[i].e.ep-rs[i].s.sp;
                        match[i] = emalloc(n+1);
       -                memmove(match[i], rs[i].sp, n);
       +                memmove(match[i], rs[i].s.sp, n);
                        match[i][n] = '\0';
                }
        }
       t@@ -66,7 +68,7 @@ clickmatch(Reprog *re, char *text, Resub rs[10], int click)
                for(i=0; i<=click; i++){
                        memset(rs, 0, 10*sizeof(Resub));
                        if(regexec(re, text+i, rs, 10))
       -                        if(rs[0].sp<=clickp && clickp<=rs[0].ep)
       +                        if(rs[0].s.sp<=clickp && clickp<=rs[0].e.ep)
                                        return 1;
                }
                return 0;
       t@@ -94,8 +96,8 @@ verbmatches(int obj, Plumbmsg *m, Rule *r, Exec *e)
                        }
                        if(!clickmatch(r->regex, m->data, rs, atoi(clickval)))
                                break;
       -                p0 = rs[0].sp - m->data;
       -                p1 = rs[0].ep - m->data;
       +                p0 = rs[0].s.sp - m->data;
       +                p1 = rs[0].e.ep - m->data;
                        if(e->p0 >=0 && !(p0==e->p0 && p1==e->p1))
                                break;
                        e->clearclick = 1;
       t@@ -120,7 +122,7 @@ verbmatches(int obj, Plumbmsg *m, Rule *r, Exec *e)
                        /* must match full text */
                        if(ntext < 0)
                                ntext = strlen(alltext);
       -                if(!regexec(r->regex, alltext, rs, 10) || rs[0].sp!=alltext || rs[0].ep!=alltext+ntext)
       +                if(!regexec(r->regex, alltext, rs, 10) || rs[0].s.sp!=alltext || rs[0].e.ep!=alltext+ntext)
                                break;
                        setvar(rs, e->match);
                        return 1;
       t@@ -389,7 +391,7 @@ enum
        {
                NARGS                = 100,
                NARGCHAR        = 8*1024,
       -        EXECSTACK         = 4096+(NARGS+1)*sizeof(char*)+NARGCHAR
       +        EXECSTACK         = 32*1024+(NARGS+1)*sizeof(char*)+NARGCHAR
        };
        
        /* copy argv to stack and free the incoming strings, so we don't leak argument vectors */
       t@@ -419,19 +421,17 @@ stackargv(char **inargv, char *argv[NARGS+1], char args[NARGCHAR])
        void
        execproc(void *v)
        {
       +        int fd[3];
                char **av;
       -        char buf[1024], *args[NARGS+1], argc[NARGCHAR];
       +        char *args[NARGS+1], argc[NARGCHAR];
        
       -        rfork(RFFDG);
       -        close(0);
       -        open("/dev/null", OREAD);
       +        fd[0] = open("/dev/null", OREAD);
       +        fd[1] = dup(1, -1);
       +        fd[2] = dup(2, -1);
                av = v;
                stackargv(av, args, argc);
                free(av);
       -        procexec(nil, args[0], args);
       -        if(args[0][0]!='/' && strncmp(args[0], "./", 2)!=0 && strncmp(args[0], "../", 3)!=0)
       -                snprint(buf, sizeof buf, "/bin/%s", args[0]);
       -        procexec(nil, buf, args);
       +        procexec(nil, fd, args[0], args);
                threadexits("can't exec");
        }
        
 (DIR) diff --git a/src/cmd/plumb/mkfile b/src/cmd/plumb/mkfile
       t@@ -1,10 +1,9 @@
       -</$objtype/mkfile
       +PLAN9=../../..
       +<$PLAN9/src/mkhdr
        
        TARG=plumber plumb
        
       -
       -BIN=/$objtype/bin
       -</sys/src/cmd/mkmany
       +<$PLAN9/src/mkmany
        
        PLUMBER=plumber.$O fsys.$O match.$O rules.$O
        PLUMB=plumb.$O
       t@@ -15,6 +14,4 @@ $PLUMB:                $HFILES
        $O.plumb:        $PLUMB
        $O.plumber:        $PLUMBER
        
       -syms:V:
       -        8c -a plumber.c        >syms
       -        8c -aa fsys.c match.c rules.c >>syms
       +LDFLAGS=$LDFLAGS -lplumb -lfs -lmux -lthread -lregexp9 -l9 -lbio -lfmt -lutf
 (DIR) diff --git a/src/cmd/plumb/plumber.c b/src/cmd/plumb/plumber.c
       t@@ -3,10 +3,10 @@
        #include <regexp.h>
        #include <thread.h>
        #include <plumb.h>
       -#include <auth.h>
        #include <fcall.h>
        #include "plumber.h"
        
       +int debug;
        char        *plumbfile;
        char *user;
        char *home;
       t@@ -47,13 +47,18 @@ threadmain(int argc, char *argv[])
                progname = "plumber";
        
                ARGBEGIN{
       +        case 'd':
       +                debug = 1;
       +                break;
                case 'p':
                        plumbfile = ARGF();
                        break;
                }ARGEND
        
       -        user = getenv("user");
       +        user = getuser();
                home = getenv("home");
       +        if(home == nil)
       +                home = getenv("HOME");
                if(user==nil || home==nil)
                        error("can't initialize $user or $home: %r");
                if(plumbfile == nil){
 (DIR) diff --git a/src/cmd/plumb/plumber.h b/src/cmd/plumb/plumber.h
       t@@ -91,3 +91,4 @@ jmp_buf        parsejmp;
        char                *lasterror;
        char                **ports;
        int                nports;
       +int                debug;
 (DIR) diff --git a/src/cmd/plumb/rules.c b/src/cmd/plumb/rules.c
       t@@ -143,7 +143,7 @@ char*
        getline(void)
        {
                static int n = 0;
       -        static char *s, *incl;
       +        static char *s /*, *incl*/;
                int c, i;
        
                i = 0;
       t@@ -414,7 +414,7 @@ include(char *s)
                t = args[1];
                fd = open(t, OREAD);
                if(fd<0 && t[0]!='/' && strncmp(t, "./", 2)!=0 && strncmp(t, "../", 3)!=0){
       -                snprint(buf, sizeof buf, "/sys/lib/plumb/%s", t);
       +                snprint(buf, sizeof buf, "#9/plumb/%s", t);
                        t = buf;
                        fd = open(t, OREAD);
                }
 (DIR) diff --git a/src/cmd/rc/plan9ish.c b/src/cmd/rc/plan9ish.c
       t@@ -27,6 +27,8 @@ char *syssigname[]={
        char*
        Rcmain(void)
        {
       +        return "#9/rcmain";
       +/*
                static char buf[256];
                char *root;
        
       t@@ -35,9 +37,10 @@ Rcmain(void)
                        root = "/usr/local/plan9";
                snprint(buf, sizeof buf, "%s/rcmain", root);
                return buf;
       +*/
        }
        
       -char Fdprefix[]="/dev/fd/";
       +char Fdprefix[]="#d/";
        void execfinit(void);
        void execbind(void);
        void execmount(void);
 (DIR) diff --git a/src/cmd/sam/unix.c b/src/cmd/sam/unix.c
       t@@ -214,92 +214,4 @@ erealloc(void *p, ulong n)
                return p;
        }
        
       -#if 0
       -char *
       -strdup(const char *s)
       -{
       -        return strcpy(emalloc(strlen(s)), s);
       -}
       -#endif
       -
       -/*
       -void exits(const char *s)
       -{
       -    if (s) fprint(2, "exit: %s\n", s);
       -    exit(s != 0);
       -}
       -
       -void
       -_exits(const char *s)
       -{
       -    if (s) fprint(2, "exit: %s\n", s);
       -    _exit(s != 0);
       -}
       -
       -int errstr(char *buf, int size)
       -{
       -    extern int errno;
       -                
       -    snprint(buf, size, "%s", strerror(errno));
       -    return 1;       
       -}                       
       -*/
       -                    
       -int
       -create(char *name, int omode, ulong perm)
       -{
       -    int mode;
       -    int fd; 
       -        
       -    if (omode & OWRITE) mode = O_WRONLY;
       -    else if (omode & OREAD) mode = O_RDONLY;
       -    else mode = O_RDWR;
       -
       -    if ((fd = open(name, mode|O_CREAT|O_TRUNC, perm)) < 0)
       -        return fd;
       -
       -    if (omode & OCEXEC)
       -        fcntl(fd, F_SETFD, fcntl(fd,F_GETFD,0) | FD_CLOEXEC);
       -
       -    /* SES - not exactly right, but hopefully good enough. */
       -    if (omode & ORCLOSE)
       -        remove(name);
       -
       -    return fd;                          
       -}
       -
       -/* SHOULD BE ELSEWHERE */
       -#if 0        /* needed on old __APPLE__ */
       -#include <lib9.h>
       -
       -Lock plk;
       -
       -ulong
       -pread(int fd, void *buf, ulong n, ulong off)
       -{
       -        ulong rv;
       -
       -        lock(&plk);
       -        if (lseek(fd, off, 0) != off)
       -                return -1;
       -        rv = read(fd, buf, n);
       -        unlock(&plk);
       -
       -        return rv;
       -}
       -
       -ulong
       -pwrite(int fd, void *buf, ulong n, ulong off)
       -{
       -        ulong rv;
       -
       -        lock(&plk);
       -        if (lseek(fd, off, 0) != off)
       -                return -1;
       -        rv = write(fd, buf, n);
       -        unlock(&plk);
       -
       -        return rv;
       -}
       -#endif
        
 (DIR) diff --git a/src/cmd/yacc.c b/src/cmd/yacc.c
       t@@ -13,8 +13,8 @@
        #define SETBIT(a,i)        ((a)[(i)>>5] |= (1<<((i)&037)))
        #define NWORDS(n)        (((n)+32)/32)
        
       -#define PARSER                "lib/yaccpar"
       -#define PARSERS                "lib/yaccpars"
       +#define PARSER                "#9/lib/yaccpar"
       +#define PARSERS                "#9/lib/yaccpars"
        #define TEMPNAME        "y.tmp.XXXXXX"
        #define ACTNAME                "y.acts.XXXXXX"
        #define OFILE                "tab.c"
       t@@ -398,19 +398,10 @@ void
        others(void)
        {
                int c, i, j;
       -        char *s, *root;
       -
       -        root = getenv("PLAN9");
       -        if(root == nil)
       -                root = "/usr/local/plan9";
       -        s = malloc(strlen(root)+1+strlen(parser)+1);
       -        strcpy(s, root);
       -        strcat(s, "/");
       -        strcat(s, parser);
       -        finput = Bopen(s, OREAD);
       +
       +        finput = Bopen(parser, OREAD);
                if(finput == 0)
       -                error("cannot find parser %s", s);
       -        free(s);
       +                error("cannot find parser %s", parser);
                warray("yyr1", levprd, nprod);
                aryfil(temp1, nprod, 0);
                PLOOP(1, i)
 (DIR) diff --git a/src/lib9/announce.c b/src/lib9/announce.c
       t@@ -5,13 +5,14 @@
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <sys/un.h>
       +#include <errno.h>
        
        #undef sun
        #define sun sockun
        extern int _p9dialparse(char*, char**, char**, u32int*, int*);
        
       -static int
       -getfd(char *dir)
       +int
       +_p9netfd(char *dir)
        {
                int fd;
        
       t@@ -83,7 +84,6 @@ p9announce(char *addr, char *dir)
                if(proto == SOCK_STREAM){
                        listen(s, 8);
                        putfd(dir, s);
       -print("announce dir: %s\n", dir);
                }
                return s;
        
       t@@ -95,9 +95,21 @@ Unix:
                        return -1;
                sn = sizeof sun;
                if(bind(s, (struct sockaddr*)&sun, sizeof sun) < 0){
       +                if(errno == EADDRINUSE
       +                && connect(s, (struct sockaddr*)&sun, sizeof sun) < 0
       +                && errno == ECONNREFUSED){
       +                        /* dead socket, so remove it */
       +                        remove(unix);
       +                        close(s);
       +                        if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
       +                                return -1;
       +                        if(bind(s, (struct sockaddr*)&sun, sizeof sun) >= 0)
       +                                goto Success;
       +                }
                        close(s);
                        return -1;
                }
       +Success:
                listen(s, 8);
                putfd(dir, s);
                return s;
       t@@ -108,18 +120,15 @@ p9listen(char *dir, char *newdir)
        {
                int fd;
        
       -        if((fd = getfd(dir)) < 0){
       +        if((fd = _p9netfd(dir)) < 0){
                        werrstr("bad 'directory' in listen: %s", dir);
                        return -1;
                }
        
       -print("accept %d", fd);
                if((fd = accept(fd, nil, nil)) < 0)
                        return -1;
       -print(" -> %d\n", fd);
        
                putfd(newdir, fd);
       -print("listen dir: %s\n", newdir);
                return fd;
        }
        
       t@@ -128,7 +137,7 @@ p9accept(int cfd, char *dir)
        {
                int fd;
        
       -        if((fd = getfd(dir)) < 0){
       +        if((fd = _p9netfd(dir)) < 0){
                        werrstr("bad 'directory' in accept");
                        return -1;
                }
 (DIR) diff --git a/src/lib9/await.c b/src/lib9/await.c
       t@@ -45,6 +45,7 @@ static struct {
        #endif
                SIGUSR1,                "sys: usr1",
                SIGUSR2,                "sys: usr2",
       +        SIGPIPE,                "sys: write on closed pipe",
        };
                
        char*
 (DIR) diff --git a/src/lib9/convM2S.c b/src/lib9/convM2S.c
       t@@ -138,6 +138,7 @@ convM2S(uchar *ap, uint nap, Fcall *f)
                        break;
        
                case Topen:
       +        case Topenfd:
                        if(p+BIT32SZ+BIT8SZ > ep)
                                return 0;
                        f->fid = GBIT32(p);
       t@@ -260,6 +261,7 @@ convM2S(uchar *ap, uint nap, Fcall *f)
                        break;
        
                case Ropen:
       +        case Ropenfd:
                case Rcreate:
                        p = gqid(p, ep, &f->qid);
                        if(p == nil)
       t@@ -268,6 +270,12 @@ convM2S(uchar *ap, uint nap, Fcall *f)
                                return 0;
                        f->iounit = GBIT32(p);
                        p += BIT32SZ;
       +                if(f->type == Ropenfd){
       +                        if(p+BIT32SZ > ep)
       +                                return 0;
       +                        f->unixfd = GBIT32(p);
       +                        p += BIT32SZ;
       +                }
                        break;
        
                case Rread:
 (DIR) diff --git a/src/lib9/convS2M.c b/src/lib9/convS2M.c
       t@@ -92,6 +92,7 @@ sizeS2M(Fcall *f)
                        break;
        
                case Topen:
       +        case Topenfd:
                        n += BIT32SZ;
                        n += BIT8SZ;
                        break;
       t@@ -164,6 +165,12 @@ sizeS2M(Fcall *f)
                        n += BIT32SZ;
                        break;
        
       +        case Ropenfd:
       +                n += QIDSZ;
       +                n += BIT32SZ;
       +                n += BIT32SZ;
       +                break;
       +
                case Rread:
                        n += BIT32SZ;
                        n += f->count;
       t@@ -257,6 +264,7 @@ convS2M(Fcall *f, uchar *ap, uint nap)
                        break;
        
                case Topen:
       +        case Topenfd:
                        PBIT32(p, f->fid);
                        p += BIT32SZ;
                        PBIT8(p, f->mode);
       t@@ -347,9 +355,14 @@ convS2M(Fcall *f, uchar *ap, uint nap)
        
                case Ropen:
                case Rcreate:
       +        case Ropenfd:
                        p = pqid(p, &f->qid);
                        PBIT32(p, f->iounit);
                        p += BIT32SZ;
       +                if(f->type == Ropenfd){
       +                        PBIT32(p, f->unixfd);
       +                        p += BIT32SZ;
       +                }
                        break;
        
                case Rread:
 (DIR) diff --git a/src/lib9/create.c b/src/lib9/create.c
       t@@ -1,8 +1,54 @@
        #include <u.h>
       +#define NOPLAN9DEFINES
        #include <libc.h>
       +#include <sys/stat.h>
       +
       +extern char *_p9translate(char*);
        
        int
       -create(char *path, int mode, ulong perm)
       +p9create(char *xpath, int mode, ulong perm)
        {
       -        return open(path, mode|O_CREAT|O_TRUNC, perm);
       +        int fd, cexec, umode, rclose;
       +        char *path;
       +
       +        if((path = _p9translate(xpath)) == nil)
       +                return -1;
       +
       +        cexec = mode&OCEXEC;
       +        rclose = mode&ORCLOSE;
       +        mode &= ~(ORCLOSE|OCEXEC);
       +
       +        /* XXX should get mode mask right? */
       +        fd = -1;
       +        if(perm&DMDIR){
       +                if(mode != OREAD){
       +                        werrstr("bad mode in directory create");
       +                        goto out;
       +                }
       +                if(mkdir(path, perm&0777) < 0)
       +                        goto out;
       +                fd = open(path, O_RDONLY);
       +        }else{
       +                umode = (mode&3)|O_CREAT|O_TRUNC;
       +                mode &= ~(3|OTRUNC);
       +                if(mode&OEXCL){
       +                        umode |= O_EXCL;
       +                        mode &= ~OEXCL;
       +                }
       +                if(mode){
       +                        werrstr("unsupported mode in create");
       +                        goto out;
       +                }
       +                fd = open(path, umode, perm);
       +        }
       +out:
       +        if(fd >= 0){
       +                if(cexec)
       +                        fcntl(fd, F_SETFL, FD_CLOEXEC);
       +                if(rclose)
       +                        remove(path);
       +        }
       +        if(path != xpath)
       +                free(path);
       +        return fd;
        }
 (DIR) diff --git a/src/lib9/fcallfmt.c b/src/lib9/fcallfmt.c
       t@@ -74,9 +74,16 @@ fcallfmt(Fmt *fmt)
                        seprint(buf, e, "Topen tag %ud fid %ud mode %d", tag, fid, f->mode);
                        break;
                case Ropen:
       -                seprint(buf, e, "Ropen tag %ud qid " QIDFMT " iounit %ud ", tag,
       +                seprint(buf, e, "Ropen tag %ud qid " QIDFMT " iounit %ud", tag,
                                f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
                        break;
       +        case Topenfd:        /* 98 */
       +                seprint(buf, e, "Topenfd tag %ud fid %ud mode %d", tag, fid, f->mode);
       +                break;
       +        case Ropenfd:
       +                seprint(buf, e, "Ropenfd tag %ud qid " QIDFMT " iounit %ud unixfd %d", tag,
       +                        f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit, f->unixfd);
       +                break;
                case Tcreate:        /* 114 */
                        seprint(buf, e, "Tcreate tag %ud fid %ud name %s perm %M mode %d", tag, fid, f->name, (ulong)f->perm, f->mode);
                        break;
 (DIR) diff --git a/src/lib9/getenv.c b/src/lib9/getenv.c
       t@@ -13,3 +13,15 @@ p9getenv(char *s)
                return strdup(t);
        }
        
       +int
       +p9putenv(char *s, char *v)
       +{
       +        char *t;
       +
       +        t = smprint("%s=%s", s, v);
       +        if(t == nil)
       +                return -1;
       +        putenv(t);
       +        free(t);
       +        return 0;
       +}
 (DIR) diff --git a/src/lib9/mkfile b/src/lib9/mkfile
       t@@ -8,6 +8,8 @@ OFILES=\
                _p9dialparse.$O\
                _p9dir.$O\
                _p9proc.$O\
       +        _p9translate.$O\
       +        access.$O\
                announce.$O\
                argv0.$O\
                atexit.$O\
       t@@ -40,11 +42,13 @@ OFILES=\
                getcallerpc-$OBJTYPE.$O\
                getenv.$O\
                getfields.$O\
       +        getns.$O\
                getuser.$O\
                getwd.$O\
                jmp.$O\
                lock.$O\
                main.$O\
       +        malloc.$O\
                malloctag.$O\
                mallocz.$O\
                nan.$O\
       t@@ -53,13 +57,18 @@ OFILES=\
                notify.$O\
                nrand.$O\
                nulldir.$O\
       +        open.$O\
       +        pipe.$O\
       +        post9p.$O\
                postnote.$O\
                qlock.$O\
                quote.$O\
       +        read9pmsg.$O\
                readn.$O\
                rendez-$SYSNAME.$O\
                rfork.$O\
                seek.$O\
       +        sendfd.$O\
                sleep.$O\
                strecpy.$O\
                sysfatal.$O\
 (DIR) diff --git a/src/lib9/notify.c b/src/lib9/notify.c
       t@@ -19,7 +19,7 @@ static int sigs[] = {
        #endif
                SIGFPE,
                SIGBUS,
       -        SIGSEGV,
       +/*        SIGSEGV,        */
                SIGSYS,
                SIGPIPE,
                SIGALRM,
 (DIR) diff --git a/src/lib9/rfork.c b/src/lib9/rfork.c
       t@@ -9,7 +9,7 @@ p9rfork(int flags)
                if((flags&(RFPROC|RFFDG|RFMEM)) == (RFPROC|RFFDG)){
                        /* check other flags before we commit */
                        flags &= ~(RFPROC|RFFDG);
       -                if(flags & ~(RFNOTEG)){
       +                if(flags & ~(RFNOTEG|RFNAMEG)){
                                werrstr("unknown flags %08ux in rfork", flags);
                                return -1;
                        }
       t@@ -17,11 +17,14 @@ p9rfork(int flags)
                        if(pid != 0)
                                return pid;
                }
       -
                if(flags&RFPROC){
       -                werrstr("cannot use rfork to fork -- use ffork");
       +                werrstr("cannot use rfork for shared memory -- use ffork");
                        return -1;
                }
       +        if(flags&RFNAMEG){
       +                /* XXX set $NAMESPACE to a new directory */
       +                flags &= ~RFNAMEG;
       +        }
                if(flags&RFNOTEG){
                        setpgid(0, getpid());
                        flags &= ~RFNOTEG;
 (DIR) diff --git a/src/libdraw/openfont.c b/src/libdraw/openfont.c
       t@@ -9,21 +9,15 @@ openfont(Display *d, char *name)
        {
                Font *fnt;
                int fd, i, n;
       -        char *buf, *nambuf, *root;
       +        char *buf, *nambuf;
        
                nambuf = 0;
                fd = open(name, OREAD);
        
                if(fd < 0 && strncmp(name, "/lib/font/bit/", 14) == 0){
       -                root = getenv("PLAN9");
       -                if(root == nil)
       -                        return 0;
       -                nambuf = malloc(strlen(root)+5+strlen(name+13)+1);
       +                nambuf = smprint("#9/font/%s", name+14);
                        if(nambuf == nil)
                                return 0;
       -                strcpy(nambuf, root);
       -                strcat(nambuf, "/font");
       -                strcat(nambuf, name+13);
                        if((fd = open(nambuf, OREAD)) < 0){
                                free(nambuf);
                                return 0;
 (DIR) diff --git a/src/libdraw/x11-alloc.c b/src/libdraw/x11-alloc.c
       t@@ -10,7 +10,7 @@
         * Allocate a Memimage with an optional pixmap backing on the X server.
         */
        Memimage*
       -xallocmemimage(Rectangle r, u32int chan, int pixmap)
       +_xallocmemimage(Rectangle r, u32int chan, int pixmap)
        {
                int d, offset;
                Memimage *m;
       t@@ -95,7 +95,7 @@ xallocmemimage(Rectangle r, u32int chan, int pixmap)
        Memimage*
        allocmemimage(Rectangle r, u32int chan)
        {
       -        return xallocmemimage(r, chan, PMundef);
       +        return _xallocmemimage(r, chan, PMundef);
        }
        
        void
 (DIR) diff --git a/src/libdraw/x11-cload.c b/src/libdraw/x11-cload.c
       t@@ -13,7 +13,7 @@ cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
        
                n = _cloadmemimage(i, r, data, ndata);
                if(n > 0 && i->X)
       -                xputxdata(i, r);
       +                _xputxdata(i, r);
                return n;
        }
        
 (DIR) diff --git a/src/libdraw/x11-draw.c b/src/libdraw/x11-draw.c
       t@@ -24,11 +24,11 @@ memimagedraw(Memimage *dst, Rectangle r, Memimage *src, Point sp,
        
                /* only fetch dst data if we need it */
                if((par->state&(Simplemask|Fullmask)) != (Simplemask|Fullmask))
       -                xgetxdata(dst, par->r);
       +                _xgetxdata(dst, par->r);
        
                /* always fetch source and mask */
       -        xgetxdata(src, par->sr);
       -        xgetxdata(mask, par->mr);
       +        _xgetxdata(src, par->sr);
       +        _xgetxdata(mask, par->mr);
        
                /* now can run memimagedraw on the in-memory bits */
                _memimagedraw(par);
       t@@ -37,7 +37,7 @@ memimagedraw(Memimage *dst, Rectangle r, Memimage *src, Point sp,
                        return;
        
                /* put bits back on x server */
       -        xputxdata(dst, par->r);
       +        _xputxdata(dst, par->r);
        }
        
        static int
       t@@ -66,7 +66,7 @@ xdraw(Memdrawparam *par)
                 */
                m = Simplesrc|Simplemask|Fullmask;
                if((state&m) == m){
       -                xfillcolor(dst, r, par->sdval);
       +                _xfillcolor(dst, r, par->sdval);
                //        xdirtyxdata(dst, r);
                        return 1;
                }
 (DIR) diff --git a/src/libdraw/x11-event.c b/src/libdraw/x11-event.c
       t@@ -48,7 +48,7 @@ eread(ulong keys, Event *e)
                        xmask |= MouseMask|StructureNotifyMask;
                if(keys&Ekeyboard){
                        xmask |= KeyPressMask;
       -                if((r = xtoplan9kbd(nil)) >= 0){
       +                if((r = _xtoplan9kbd(nil)) >= 0){
                                e->kbdc = r;
                                return Ekeyboard;
                        }
       t@@ -60,24 +60,24 @@ again:
        
                switch(xevent.type){
                case Expose:
       -                xexpose(&xevent, _x.display);
       +                _xexpose(&xevent, _x.display);
                        goto again;
                case DestroyNotify:
       -                if(xdestroy(&xevent, _x.display))
       +                if(_xdestroy(&xevent, _x.display))
                                postnote(PNGROUP, getpgrp(), "hangup");
                        goto again;
                case ConfigureNotify:
       -                if(xconfigure(&xevent, _x.display))
       +                if(_xconfigure(&xevent, _x.display))
                                eresized(1);
                        goto again;
                case ButtonPress:
                case ButtonRelease:
                case MotionNotify:
       -                if(xtoplan9mouse(_x.display, &xevent, &e->mouse) < 0)
       +                if(_xtoplan9mouse(_x.display, &xevent, &e->mouse) < 0)
                                goto again;
                        return Emouse;
                case KeyPress:
       -                e->kbdc = xtoplan9kbd(&xevent);
       +                e->kbdc = _xtoplan9kbd(&xevent);
                        if(e->kbdc == -1)
                                goto again;
                        return Ekeyboard;
       t@@ -136,7 +136,7 @@ ecanmouse(void)
                eflush();
        again:
                if(XCheckWindowEvent(_x.display, _x.drawable, MouseMask, &xe)){
       -                if(xtoplan9mouse(_x.display, &xe, &m) < 0)
       +                if(_xtoplan9mouse(_x.display, &xe, &m) < 0)
                                goto again;
                        XPutBackEvent(_x.display, &xe);
                        return 1;
       t@@ -151,13 +151,13 @@ ecankbd(void)
                int r;
        
                eflush();
       -        if((r = xtoplan9kbd(nil)) >= 0){
       -                xtoplan9kbd((XEvent*)-1);
       +        if((r = _xtoplan9kbd(nil)) >= 0){
       +                _xtoplan9kbd((XEvent*)-1);
                        return 1;
                }
        again:
                if(XCheckWindowEvent(_x.display, _x.drawable, KeyPressMask, &xe)){
       -                if(xtoplan9kbd(&xe) == -1)
       +                if(_xtoplan9kbd(&xe) == -1)
                                goto again;
                        XPutBackEvent(_x.display, &xe);
                        return 1;
       t@@ -168,12 +168,12 @@ again:
        void
        emoveto(Point p)
        {
       -        xmoveto(p);
       +        _xmoveto(p);
        }
        
        void
        esetcursor(Cursor *c)
        {
       -        xsetcursor(c);
       +        _xsetcursor(c);
        }
        
 (DIR) diff --git a/src/libdraw/x11-fill.c b/src/libdraw/x11-fill.c
       t@@ -13,13 +13,13 @@ memfillcolor(Memimage *m, u32int val)
                if(m->X == nil)
                        return;
                if((val & 0xFF) == 0xFF)        /* full alpha */
       -                xfillcolor(m, m->r, _rgbatoimg(m, val));
       +                _xfillcolor(m, m->r, _rgbatoimg(m, val));
                else
       -                xputxdata(m, m->r);
       +                _xputxdata(m, m->r);
        }
        
        void
       -xfillcolor(Memimage *m, Rectangle r, u32int v)
       +_xfillcolor(Memimage *m, Rectangle r, u32int v)
        {
                Point p;
                Xmem *xm;
 (DIR) diff --git a/src/libdraw/x11-get.c b/src/libdraw/x11-get.c
       t@@ -16,7 +16,7 @@ addrect(Rectangle *rp, Rectangle r)
        }
        
        XImage*
       -xgetxdata(Memimage *m, Rectangle r)
       +_xgetxdata(Memimage *m, Rectangle r)
        {
                int x, y;
                uchar *p;
       t@@ -55,7 +55,7 @@ xgetxdata(Memimage *m, Rectangle r)
        }
        
        void
       -xputxdata(Memimage *m, Rectangle r)
       +_xputxdata(Memimage *m, Rectangle r)
        {
                int offset, x, y;
                uchar *p;
       t@@ -97,7 +97,7 @@ xputxdata(Memimage *m, Rectangle r)
        }
        
        void
       -xdirtyxdata(Memimage *m, Rectangle r)
       +_xdirtyxdata(Memimage *m, Rectangle r)
        {
                Xmem *xm;
        
 (DIR) diff --git a/src/libdraw/x11-init.c b/src/libdraw/x11-init.c
       t@@ -359,7 +359,7 @@ xattach(char *label)
                _x.screenr = r;
                _x.screenpm = XCreatePixmap(_x.display, _x.drawable, Dx(r), Dy(r), _x.depth);
                _x.nextscreenpm = _x.screenpm;
       -        _x.screenimage = xallocmemimage(r, _x.chan, _x.screenpm);
       +        _x.screenimage = _xallocmemimage(r, _x.chan, _x.screenpm);
        
                /*
                 * Allocate some useful graphics contexts for the future.
       t@@ -651,7 +651,7 @@ flushmemscreen(Rectangle r)
        }
        
        void
       -xexpose(XEvent *e, XDisplay *xd)
       +_xexpose(XEvent *e, XDisplay *xd)
        {
                XExposeEvent *xe;
                Rectangle r;
       t@@ -673,7 +673,7 @@ xexpose(XEvent *e, XDisplay *xd)
        }
        
        int
       -xdestroy(XEvent *e, XDisplay *xd)
       +_xdestroy(XEvent *e, XDisplay *xd)
        {
                XDestroyWindowEvent *xe;
        
       t@@ -686,7 +686,7 @@ xdestroy(XEvent *e, XDisplay *xd)
        }
        
        int
       -xconfigure(XEvent *e, XDisplay *xd)
       +_xconfigure(XEvent *e, XDisplay *xd)
        {
                Rectangle r;
                XConfigureEvent *xe = (XConfigureEvent*)e;
       t@@ -719,7 +719,7 @@ xreplacescreenimage(void)
                        return 0;
        
                pixmap = XCreatePixmap(_x.display, _x.drawable, Dx(r), Dy(r), _x.depth);
       -        m = xallocmemimage(r, _x.chan, pixmap);
       +        m = _xallocmemimage(r, _x.chan, pixmap);
                if(_x.nextscreenpm != _x.screenpm)
                        XFreePixmap(_x.display, _x.nextscreenpm);
                _x.nextscreenpm = pixmap;
 (DIR) diff --git a/src/libdraw/x11-itrans.c b/src/libdraw/x11-itrans.c
       t@@ -12,7 +12,7 @@
        #include "x11-memdraw.h"
        
        static int
       -_xtoplan9kbd(XEvent *e)
       +__xtoplan9kbd(XEvent *e)
        {
                int ind, k, md;
        
       t@@ -125,7 +125,7 @@ xtoplan9latin1(XEvent *e)
                int n;
                int r;
        
       -        r = _xtoplan9kbd(e);
       +        r = __xtoplan9kbd(e);
                if(r < 0)
                        return nil;
                if(alting){
       t@@ -156,7 +156,7 @@ xtoplan9latin1(XEvent *e)
        }
        
        int
       -xtoplan9kbd(XEvent *e)
       +_xtoplan9kbd(XEvent *e)
        {
                static Rune *r;
        
       t@@ -173,7 +173,7 @@ xtoplan9kbd(XEvent *e)
        }
        
        int
       -xtoplan9mouse(XDisplay *xd, XEvent *e, Mouse *m)
       +_xtoplan9mouse(XDisplay *xd, XEvent *e, Mouse *m)
        {
                int s;
                XButtonEvent *be;
       t@@ -260,7 +260,7 @@ xtoplan9mouse(XDisplay *xd, XEvent *e, Mouse *m)
        }
        
        void
       -xmoveto(Point p)
       +_xmoveto(Point p)
        {
                XWarpPointer(_x.display, None, _x.drawable, 0, 0, 0, 0, p.x, p.y);
                XFlush(_x.display);
       t@@ -296,7 +296,7 @@ xcursorarrow(void)
        
        
        void
       -xsetcursor(Cursor *c)
       +_xsetcursor(Cursor *c)
        {
                XColor fg, bg;
                XCursor xc;
       t@@ -335,7 +335,7 @@ struct {
        } clip;
        
        char*
       -xgetsnarf(XDisplay *xd)
       +_xgetsnarf(XDisplay *xd)
        {
                uchar *data, *xdata;
                Atom clipboard, type, prop;
       t@@ -420,7 +420,7 @@ out:
        }
        
        void
       -xputsnarf(XDisplay *xd, char *data)
       +_xputsnarf(XDisplay *xd, char *data)
        {
                XButtonEvent e;
        
       t@@ -445,7 +445,7 @@ xputsnarf(XDisplay *xd, char *data)
        }
        
        int
       -xselect(XEvent *e, XDisplay *xd)
       +_xselect(XEvent *e, XDisplay *xd)
        {
                char *name;
                XEvent r;
       t@@ -493,12 +493,12 @@ if(0) fprint(2, "xselect target=%d requestor=%d property=%d selection=%d\n",
        void
        putsnarf(char *data)
        {
       -        xputsnarf(_x.snarfcon, data);
       +        _xputsnarf(_x.snarfcon, data);
        }
        
        char*
        getsnarf(void)
        {
       -        return xgetsnarf(_x.snarfcon);
       +        return _xgetsnarf(_x.snarfcon);
        }
        
 (DIR) diff --git a/src/libdraw/x11-keyboard.c b/src/libdraw/x11-keyboard.c
       t@@ -46,12 +46,12 @@ _ioproc(void *arg)
                        XWindowEvent(_x.kbdcon, _x.drawable, KeyPressMask, &xevent);
                        switch(xevent.type){
                        case KeyPress:
       -                        i = xtoplan9kbd(&xevent);
       +                        i = _xtoplan9kbd(&xevent);
                                if(i == -1)
                                        continue;
                                r = i;
                                send(kc->c, &r);
       -                        while((i=xtoplan9kbd(nil)) >= 0){
       +                        while((i=_xtoplan9kbd(nil)) >= 0){
                                        r = i;
                                        send(kc->c, &r);
                                }
 (DIR) diff --git a/src/libdraw/x11-load.c b/src/libdraw/x11-load.c
       t@@ -13,7 +13,7 @@ loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
        
                n = _loadmemimage(i, r, data, ndata);
                if(n > 0 && i->X)
       -                xputxdata(i, r);
       +                _xputxdata(i, r);
                return n;
        }
        
 (DIR) diff --git a/src/libdraw/x11-memdraw.h b/src/libdraw/x11-memdraw.h
       t@@ -76,26 +76,26 @@ struct Xprivate {
        
        extern Xprivate _x;
        
       -extern Memimage *xallocmemimage(Rectangle, u32int, int);
       -extern XImage        *xallocxdata(Memimage*, Rectangle);
       -extern void        xdirtyxdata(Memimage*, Rectangle);
       -extern void        xfillcolor(Memimage*, Rectangle, u32int);
       -extern void        xfreexdata(Memimage*);
       -extern XImage        *xgetxdata(Memimage*, Rectangle);
       -extern void        xputxdata(Memimage*, Rectangle);
       +extern Memimage *_xallocmemimage(Rectangle, u32int, int);
       +extern XImage        *_xallocxdata(Memimage*, Rectangle);
       +extern void        _xdirtyxdata(Memimage*, Rectangle);
       +extern void        _xfillcolor(Memimage*, Rectangle, u32int);
       +extern void        _xfreexdata(Memimage*);
       +extern XImage        *_xgetxdata(Memimage*, Rectangle);
       +extern void        _xputxdata(Memimage*, Rectangle);
        extern void        _initdisplaymemimage(Display*, Memimage*);
        
        struct Mouse;
       -extern int        xtoplan9mouse(XDisplay*, XEvent*, struct Mouse*);
       -extern int        xtoplan9kbd(XEvent*);
       -extern void        xexpose(XEvent*, XDisplay*);
       -extern int        xselect(XEvent*, XDisplay*);
       -extern int        xconfigure(XEvent*, XDisplay*);
       -extern int        xdestroy(XEvent*, XDisplay*);
       -extern void        flushmemscreen(Rectangle);
       -extern void        xmoveto(Point);
       +extern int        _xtoplan9mouse(XDisplay*, XEvent*, struct Mouse*);
       +extern int        _xtoplan9kbd(XEvent*);
       +extern void        _xexpose(XEvent*, XDisplay*);
       +extern int        _xselect(XEvent*, XDisplay*);
       +extern int        _xconfigure(XEvent*, XDisplay*);
       +extern int        _xdestroy(XEvent*, XDisplay*);
       +extern void        _flushmemscreen(Rectangle);
       +extern void        _xmoveto(Point);
        struct Cursor;
       -extern void        xsetcursor(struct Cursor*);
       +extern void        _xsetcursor(struct Cursor*);
        
        #define MouseMask (\
                ButtonPressMask|\
 (DIR) diff --git a/src/libdraw/x11-mouse.c b/src/libdraw/x11-mouse.c
       t@@ -11,7 +11,7 @@
        void
        moveto(Mousectl *m, Point pt)
        {
       -        xmoveto(pt);
       +        _xmoveto(pt);
        }
        
        void
       t@@ -64,10 +64,10 @@ _ioproc(void *arg)
                        XNextEvent(_x.mousecon, &xevent);
                        switch(xevent.type){
                        case Expose:
       -                        xexpose(&xevent, _x.mousecon);
       +                        _xexpose(&xevent, _x.mousecon);
                                continue;
                        case DestroyNotify:
       -                        if(xdestroy(&xevent, _x.mousecon)){
       +                        if(_xdestroy(&xevent, _x.mousecon)){
                                        /* drain it before sending */
                                        /* apps that care can notice we sent a 0 */
                                        /* otherwise we'll have getwindow send SIGHUP */
       t@@ -77,16 +77,16 @@ _ioproc(void *arg)
                                }
                                continue;
                        case ConfigureNotify:
       -                        if(xconfigure(&xevent, _x.mousecon))
       +                        if(_xconfigure(&xevent, _x.mousecon))
                                        nbsend(mc->resizec, &one);
                                continue;
                        case SelectionRequest:
       -                        xselect(&xevent, _x.mousecon);
       +                        _xselect(&xevent, _x.mousecon);
                                continue;
                        case ButtonPress:
                        case ButtonRelease:
                        case MotionNotify:
       -                        if(xtoplan9mouse(_x.mousecon, &xevent, &m) < 0)
       +                        if(_xtoplan9mouse(_x.mousecon, &xevent, &m) < 0)
                                        continue;
                                send(mc->c, &m);
                                /*
       t@@ -117,6 +117,6 @@ initmouse(char *file, Image *i)
        void
        setcursor(Mousectl *mc, Cursor *c)
        {
       -        xsetcursor(c);
       +        _xsetcursor(c);
        }
        
 (DIR) diff --git a/src/libdraw/x11-pixelbits.c b/src/libdraw/x11-pixelbits.c
       t@@ -10,7 +10,7 @@ u32int
        pixelbits(Memimage *m, Point p)
        {
                if(m->X)
       -                xgetxdata(m, Rect(p.x, p.y, p.x+1, p.y+1));
       +                _xgetxdata(m, Rect(p.x, p.y, p.x+1, p.y+1));
                return _pixelbits(m, p);
        }
        
 (DIR) diff --git a/src/libdraw/x11-unload.c b/src/libdraw/x11-unload.c
       t@@ -10,7 +10,7 @@ int
        unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
        {
                if(i->X)
       -                xgetxdata(i, r);
       +                _xgetxdata(i, r);
                return _unloadmemimage(i, r, data, ndata);
        }
        
 (DIR) diff --git a/src/libfs/fs.c b/src/libfs/fs.c
       t@@ -50,7 +50,7 @@ fsroot(Fsys *fs)
        }
        
        Fsys*
       -fsmount(int fd)
       +fsmount(int fd, char *aname)
        {
                int n;
                char *user;
       t@@ -62,13 +62,14 @@ fsmount(int fd)
                strcpy(fs->version, "9P2000");
                if((n = fsversion(fs, 8192, fs->version, sizeof fs->version)) < 0){
                Error:
       +                fs->fd = -1;
                        fsunmount(fs);
                        return nil;
                }
                fs->msize = n;
        
                user = getuser();
       -        if((fs->root = fsattach(fs, nil, getuser(), "")) == nil)
       +        if((fs->root = fsattach(fs, nil, getuser(), aname)) == nil)
                        goto Error;
                return fs;
        }
       t@@ -76,6 +77,8 @@ fsmount(int fd)
        void
        fsunmount(Fsys *fs)
        {
       +        fsclose(fs->root);
       +        fs->root = nil;
                _fsdecref(fs);
        }
        
       t@@ -85,7 +88,9 @@ _fsdecref(Fsys *fs)
                Fid *f, *next;
        
                qlock(&fs->lk);
       -        if(--fs->ref == 0){
       +        --fs->ref;
       +        //fprint(2, "fsdecref %p to %d\n", fs, fs->ref);
       +        if(fs->ref == 0){
                        close(fs->fd);
                        for(f=fs->freefid; f; f=next){
                                next = f->next;
       t@@ -103,6 +108,7 @@ fsversion(Fsys *fs, int msize, char *version, int nversion)
                void *freep;
                Fcall tx, rx;
        
       +        tx.tag = 0;
                tx.type = Tversion;
                tx.version = version;
                tx.msize = msize;
       t@@ -120,9 +126,13 @@ fsattach(Fsys *fs, Fid *afid, char *user, char *aname)
                Fcall tx, rx;
                Fid *fid;
        
       +        if(aname == nil)
       +                aname = "";
       +
                if((fid = _fsgetfid(fs)) == nil)
                        return nil;
        
       +        tx.tag = 0;
                tx.type = Tattach;
                tx.afid = afid ? afid->fid : NOFID;
                tx.fid = fid->fid;
       t@@ -145,12 +155,11 @@ fsrpc(Fsys *fs, Fcall *tx, Fcall *rx, void **freep)
        
                n = sizeS2M(tx);
                tpkt = malloc(n);
       -fprint(2, "tpkt %p\n", tpkt);
                if(freep)
                        *freep = nil;
                if(tpkt == nil)
                        return -1;
       -        fprint(2, "<- %F\n", tx);
       +        //fprint(2, "<- %F\n", tx);
                nn = convS2M(tx, tpkt, n);
                if(nn != n){
                        free(tpkt);
       t@@ -159,20 +168,18 @@ fprint(2, "tpkt %p\n", tpkt);
                        return -1;
                }
                rpkt = muxrpc(&fs->mux, tpkt);
       -fprint(2, "tpkt %p\n", tpkt);
                free(tpkt);
       -fprint(2, "tpkt freed\n");
                if(rpkt == nil)
                        return -1;
                n = GBIT32((uchar*)rpkt);
                nn = convM2S(rpkt, n, rx);
                if(nn != n){
                        free(rpkt);
       -                werrstr("libfs: convM2S packet size mismatch");
       +                werrstr("libfs: convM2S packet size mismatch %d %d", n, nn);
                        fprint(2, "%r\n");
                        return -1;
                }
       -        fprint(2, "-> %F\n", rx);
       +        //fprint(2, "-> %F\n", rx);
                if(rx->type == Rerror){
                        werrstr("%s", rx->ename);
                        free(rpkt);
       t@@ -208,13 +215,13 @@ _fsgetfid(Fsys *fs)
                                f[i].fid = fs->nextfid++;
                                f[i].next = &f[i+1];
                                f[i].fs = fs;
       -                        fs->ref++;
                        }
                        f[i-1].next = nil;
                        fs->freefid = f;
                }
                f = fs->freefid;
                fs->freefid = f->next;
       +        fs->ref++;
                qunlock(&fs->lk);
                return f;
        }
       t@@ -259,7 +266,7 @@ _fsrecv(Mux *mux)
        {
                uchar *pkt;
                uchar buf[4];
       -        int n;
       +        int n, nfd;
                Fsys *fs;
        
                fs = mux->aux;
       t@@ -277,11 +284,13 @@ _fsrecv(Mux *mux)
                        free(pkt);
                        return nil;
                }
       -#if 0
                if(pkt[4] == Ropenfd){
       -                /* do unix socket crap */
       -                sysfatal("no socket crap implemented");
       +                if((nfd=recvfd(fs->fd)) < 0){
       +                        fprint(2, "recv fd error: %r\n");
       +                        free(pkt);
       +                        return nil;
       +                }
       +                PBIT32(pkt+n-4, nfd);
                }
       -#endif
                return pkt;
        }
 (DIR) diff --git a/src/libfs/mkfile b/src/libfs/mkfile
       t@@ -8,7 +8,9 @@ OFILES=\
                create.$O\
                dirread.$O\
                fs.$O\
       +        ns.$O\
                open.$O\
       +        openfd.$O\
                read.$O\
                stat.$O\
                walk.$O\
 (DIR) diff --git a/src/libmux/mux.c b/src/libmux/mux.c
       t@@ -173,6 +173,5 @@ puttag(Mux *mux, Muxrpc *r)
                mux->nwait--;
                mux->freetag = i;
                rwakeup(&mux->tagrend);
       -fprint(2, "free %p\n", r);
                free(r);
        }
 (DIR) diff --git a/src/libplumb/mesg.c b/src/libplumb/mesg.c
       t@@ -1,5 +1,7 @@
        #include <u.h>
        #include <libc.h>
       +#include <fcall.h>
       +#include <fs.h>
        #include "plumb.h"
        
        static char attrbuf[4096];
       t@@ -9,35 +11,15 @@ char *home;
        int
        plumbopen(char *name, int omode)
        {
       -#if 0
       -        int fd, f;
       -        char *s;
       -#endif
       -        char buf[256];
       +        Fsys *fs;
       +        int fd;
        
       -        if(name[0] == '/')
       -                return open(name, omode);
       -        if(home == nil){
       -                home = getenv("HOME");
       -                if(home == nil)
       -                        return -1;
       -        }
       -        snprint(buf, sizeof buf, "%s/mnt/plumb", home);
       -#if 0
       -        fd = open(buf, omode);
       -        if(fd >= 0)
       -                return fd;
       -        snprint(buf, sizeof buf, "/mnt/term/mnt/plumb/%s", name);
       -        fd = open(buf, omode);
       -        if(fd >= 0)
       -                return fd;
       -        /* try mounting service */
       -        s = getenv("plumbsrv");
       -        if(s == nil)
       +        fs = nsmount("plumb", "");
       +        if(fs == nil)
                        return -1;
       -        snprint(buf, sizeof buf, "/mnt/plumb/%s", name);
       -#endif
       -        return open(buf, omode);
       +        fd = fsopenfd(fs, name, omode);
       +        fsunmount(fs);
       +        return fd;
        }
        
        static int
 (DIR) diff --git a/src/libthread/asm-FreeBSD-386.s b/src/libthread/asm-FreeBSD-386.s
       t@@ -41,9 +41,9 @@ _xdec:
                movl 4(%esp), %eax
                lock decl 0(%eax)
                jz iszero
       -        movl %eax, 1
       +        movl $1, %eax
                ret
        iszero:
       -        movl %eax, 0
       +        movl $0, %eax
                ret
        
 (DIR) diff --git a/src/libthread/create.c b/src/libthread/create.c
       t@@ -1,8 +1,7 @@
        #include "threadimpl.h"
        
        Pqueue _threadpq;
       -
       -int _threadmultiproc;
       +int _threadprocs;
        
        static int nextID(void);
        
       t@@ -90,7 +89,6 @@ proccreate(void (*f)(void*), void *arg, uint stacksize)
                        werrstr("cannot create procs once there is an idle thread");
                        return -1;
                }
       -        _threadmultiproc = 1;
                return procrfork(f, arg, stacksize, 0);
        }
        
       t@@ -125,11 +123,12 @@ threadcreateidle(void (*f)(void *arg), void *arg, uint stacksize)
        {
                int id;
        
       -        if(_threadmultiproc){
       +        if(_threadprocs!=1){
                        werrstr("cannot have idle thread in multi-proc program");
                        return -1;
                }
                id = newthread(_threadgetproc(), f, arg, stacksize, nil, threadgetgrp());
       +        _threaddebug(DBGSCHED, "idle is %d", id);
                _threadidle();
                return id;
        }
       t@@ -154,6 +153,7 @@ _newproc(void (*f)(void *arg), void *arg, uint stacksize, char *name, int grp, i
                else
                        *_threadpq.tail = p;
                _threadpq.tail = &p->next;
       +        _threadprocs++;
                unlock(&_threadpq.lock);
                return p;
        }
 (DIR) diff --git a/src/libthread/exec-unix.c b/src/libthread/exec-unix.c
       t@@ -3,7 +3,7 @@
        #include "threadimpl.h"
        
        void
       -procexec(Channel *pidc, char *prog, char *args[])
       +procexec(Channel *pidc, int fd[3], char *prog, char *args[])
        {
                int n;
                Proc *p;
       t@@ -45,6 +45,7 @@ procexec(Channel *pidc, char *prog, char *args[])
                assert(p->needexec==0);
                p->exec.prog = prog;
                p->exec.args = args;
       +        p->exec.stdfd = fd;
                p->needexec = 1;
                _sched();
        
       t@@ -56,7 +57,11 @@ procexec(Channel *pidc, char *prog, char *args[])
                        goto Bad;
                }
                close(p->exec.fd[0]);
       -
       +        close(fd[0]);
       +        if(fd[1] != fd[0])
       +                close(fd[1]);
       +        if(fd[2] != fd[1] && fd[2] != fd[0])
       +                close(fd[2]);
                if(pidc)
                        sendul(pidc, t->ret);
        
       t@@ -66,9 +71,9 @@ procexec(Channel *pidc, char *prog, char *args[])
        }
        
        void
       -procexecl(Channel *pidc, char *f, ...)
       +procexecl(Channel *pidc, int fd[3], char *f, ...)
        {
       -        procexec(pidc, f, &f+1);
       +        procexec(pidc, fd, f, &f+1);
        }
        
        void
       t@@ -107,10 +112,17 @@ efork(void *ve)
        {
                char buf[ERRMAX];
                Execargs *e;
       +        int i;
        
                e = ve;
                _threaddebug(DBGEXEC, "_schedexec %s -- calling execv", e->prog);
       -        execv(e->prog, e->args);
       +        dup(e->stdfd[0], 0);
       +        dup(e->stdfd[1], 1);
       +        dup(e->stdfd[2], 2);
       +        for(i=3; i<40; i++)
       +                if(i != e->fd[1])
       +                        close(i);
       +        execvp(e->prog, e->args);
                _threaddebug(DBGEXEC, "_schedexec failed: %r");
                rerrstr(buf, sizeof buf);
                if(buf[0]=='\0')
 (DIR) diff --git a/src/libthread/exec.c b/src/libthread/exec.c
       t@@ -3,7 +3,7 @@
        #define PIPEMNT        "/mnt/temp"
        
        void
       -procexec(Channel *pidc, char *prog, char *args[])
       +procexec(Channel *pidc, int fd[3], char *prog, char *args[])
        {
                int n;
                Proc *p;
       t@@ -50,6 +50,7 @@ procexec(Channel *pidc, char *prog, char *args[])
                assert(p->needexec==0);
                p->exec.prog = prog;
                p->exec.args = args;
       +        p->exec.stdfd = fd;
                p->needexec = 1;
                _sched();
        
       t@@ -61,7 +62,11 @@ procexec(Channel *pidc, char *prog, char *args[])
                        goto Bad;
                }
                close(p->exec.fd[0]);
       -
       +        close(fd[0]);
       +        if(fd[1] != fd[0])
       +                close(fd[1]);
       +        if(fd[2] != fd[1] && fd[2] != fd[0])
       +                close(fd[2]);
                if(pidc)
                        sendul(pidc, t->ret);
        
       t@@ -70,8 +75,8 @@ procexec(Channel *pidc, char *prog, char *args[])
        }
        
        void
       -procexecl(Channel *pidc, char *f, ...)
       +procexecl(Channel *pidc, int fd[3], char *f, ...)
        {
       -        procexec(pidc, f, &f+1);
       +        procexec(pidc, fd, f, &f+1);
        }
        
 (DIR) diff --git a/src/libthread/main.c b/src/libthread/main.c
       t@@ -37,7 +37,7 @@ main(int argc, char **argv)
                _systhreadinit();
                _qlockinit(_threadrendezvous);
                _sysfatal = _threadsysfatal;
       -//        notify(_threadnote);
       +        notify(_threadnote);
                if(mainstacksize == 0)
                        mainstacksize = 32*1024;
        
       t@@ -98,6 +98,7 @@ _schedexit(Proc *p)
                                break;
                        }
                }
       +        _threadprocs--;
                unlock(&_threadpq.lock);
        
                strncpy(ex, p->exitstr, sizeof ex);
 (DIR) diff --git a/src/libthread/note.c b/src/libthread/note.c
       t@@ -2,7 +2,6 @@
        
        int        _threadnopasser;
        
       -#ifdef NOTDEF
        #define        NFN                33
        #define        ERRLEN        48
        typedef struct Note Note;
       t@@ -85,7 +84,7 @@ _threadnote(void *v, char *s)
                Note *n;
        
                _threaddebug(DBGNOTE, "Got note %s", s);
       -        if(strncmp(s, "sys:", 4) == 0)
       +        if(strncmp(s, "sys:", 4) == 0 && strcmp(s, "sys: write on closed pipe") != 0)
                        noted(NDFLT);
        
        //        if(_threadexitsallstatus){
       t@@ -112,7 +111,6 @@ _threadnote(void *v, char *s)
                        delayednotes(p, v);
                noted(NCONT);
        }
       -#endif
        
        int
        _procsplhi(void)
 (DIR) diff --git a/src/libthread/sched.c b/src/libthread/sched.c
       t@@ -98,17 +98,18 @@ runthread(Proc *p)
                q = &p->ready;
                lock(&p->readylock);
                if(q->head == nil){
       -                q->asleep = 1;
                        if(p->idle){
                                if(p->idle->state != Ready){
                                        fprint(2, "everyone is asleep\n");
                                        exits("everyone is asleep");
                                }
                                unlock(&p->readylock);
       +                        _threaddebug(DBGSCHED, "running idle thread", p->nthreads);
                                return p->idle;
                        }
        
                        _threaddebug(DBGSCHED, "sleeping for more work (%d threads)", p->nthreads);
       +                q->asleep = 1;
                        unlock(&p->readylock);
                        while(rendezvous((ulong)q, 0) == ~0){
                                if(_threadexitsallstatus)
       t@@ -148,7 +149,7 @@ Resched:
                                _threaddelproc();
                                _schedexit(p);
                        }
       -        //        _threaddebug(DBGSCHED, "running %d.%d", t->proc->pid, t->id);
       +                _threaddebug(DBGSCHED, "running %d.%d", t->proc->pid, t->id);
                        p->thread = t;
                        if(t->moribund){
                                _threaddebug(DBGSCHED, "%d.%d marked to die");
       t@@ -176,8 +177,10 @@ _threadready(Thread *t)
        {
                Tqueue *q;
        
       -        if(t == t->proc->idle)
       +        if(t == t->proc->idle){
       +                _threaddebug(DBGSCHED, "idle thread is ready");
                        return;
       +        }
        
                assert(t->state == Ready);
                _threaddebug(DBGSCHED, "readying %d.%d", t->proc->pid, t->id);
       t@@ -206,18 +209,25 @@ void
        _threadidle(void)
        {
                Tqueue *q;
       -        Thread *t;
       +        Thread *t, *idle;
                Proc *p;
        
                p = _threadgetproc();
                q = &p->ready;
                lock(&p->readylock);
       -        assert(q->head);
       -        t = q->head;
       -        q->head = t->next;
       -        if(q->tail == t)
       +        assert(q->tail);
       +        idle = q->tail;
       +        if(q->head == idle){
       +                q->head = nil;
                        q->tail = nil;
       -        p->idle = t;
       +        }else{
       +                for(t=q->head; t->next!=q->tail; t=t->next)
       +                        ;
       +                t->next = nil;
       +                q->tail = t;
       +        }
       +        p->idle = idle;
       +        _threaddebug(DBGSCHED, "p->idle is %d\n", idle->id);
                unlock(&p->readylock);
        }
        
 (DIR) diff --git a/src/libthread/threadimpl.h b/src/libthread/threadimpl.h
       t@@ -105,6 +105,7 @@ struct Execargs
                char                *prog;
                char                **args;
                int                fd[2];
       +        int                *stdfd;
        };
        
        struct Proc
       t@@ -214,4 +215,5 @@ extern void _stackfree(void*);
        extern int _threadgetpid(void);
        extern void _threadmemset(void*, int, int);
        extern void _threaddebugmemset(void*, int, int);
       +extern int _threadprocs;