summaryrefslogtreecommitdiff
path: root/sys/src/ape/lib/ap/plan9
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/ape/lib/ap/plan9
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/ape/lib/ap/plan9')
-rwxr-xr-xsys/src/ape/lib/ap/plan9/9errstr.c4
-rwxr-xr-xsys/src/ape/lib/ap/plan9/9iounit.c65
-rwxr-xr-xsys/src/ape/lib/ap/plan9/9mallocz.c14
-rwxr-xr-xsys/src/ape/lib/ap/plan9/9read.c11
-rwxr-xr-xsys/src/ape/lib/ap/plan9/9readn.c21
-rwxr-xr-xsys/src/ape/lib/ap/plan9/9wait.c93
-rwxr-xr-xsys/src/ape/lib/ap/plan9/9write.c11
-rwxr-xr-xsys/src/ape/lib/ap/plan9/_buf.c472
-rwxr-xr-xsys/src/ape/lib/ap/plan9/_dirconv.c65
-rwxr-xr-xsys/src/ape/lib/ap/plan9/_envsetup.c123
-rwxr-xr-xsys/src/ape/lib/ap/plan9/_errno.c124
-rwxr-xr-xsys/src/ape/lib/ap/plan9/_exit.c58
-rwxr-xr-xsys/src/ape/lib/ap/plan9/_fcall.c422
-rwxr-xr-xsys/src/ape/lib/ap/plan9/_fdinfo.c169
-rwxr-xr-xsys/src/ape/lib/ap/plan9/_getpw.c174
-rwxr-xr-xsys/src/ape/lib/ap/plan9/_nap.c20
-rwxr-xr-xsys/src/ape/lib/ap/plan9/access.c62
-rwxr-xr-xsys/src/ape/lib/ap/plan9/acid.c31
-rwxr-xr-xsys/src/ape/lib/ap/plan9/acidlib706
-rwxr-xr-xsys/src/ape/lib/ap/plan9/alarm.c9
-rwxr-xr-xsys/src/ape/lib/ap/plan9/brk.c36
-rwxr-xr-xsys/src/ape/lib/ap/plan9/buf.prom360
-rwxr-xr-xsys/src/ape/lib/ap/plan9/cfgetospeed.c26
-rwxr-xr-xsys/src/ape/lib/ap/plan9/chdir.c14
-rwxr-xr-xsys/src/ape/lib/ap/plan9/chmod.c33
-rwxr-xr-xsys/src/ape/lib/ap/plan9/chown.c39
-rwxr-xr-xsys/src/ape/lib/ap/plan9/close.c34
-rwxr-xr-xsys/src/ape/lib/ap/plan9/convD2M.c93
-rwxr-xr-xsys/src/ape/lib/ap/plan9/convM2D.c74
-rwxr-xr-xsys/src/ape/lib/ap/plan9/creat.c13
-rwxr-xr-xsys/src/ape/lib/ap/plan9/ctermid.c20
-rwxr-xr-xsys/src/ape/lib/ap/plan9/ctime.c321
-rwxr-xr-xsys/src/ape/lib/ap/plan9/cuserid.c21
-rwxr-xr-xsys/src/ape/lib/ap/plan9/dir.h80
-rwxr-xr-xsys/src/ape/lib/ap/plan9/dirread.c119
-rwxr-xr-xsys/src/ape/lib/ap/plan9/dirstat.c107
-rwxr-xr-xsys/src/ape/lib/ap/plan9/dirtostat.c52
-rwxr-xr-xsys/src/ape/lib/ap/plan9/dup.c24
-rwxr-xr-xsys/src/ape/lib/ap/plan9/execl.c9
-rwxr-xr-xsys/src/ape/lib/ap/plan9/execle.c11
-rwxr-xr-xsys/src/ape/lib/ap/plan9/execlp.c24
-rwxr-xr-xsys/src/ape/lib/ap/plan9/execv.c9
-rwxr-xr-xsys/src/ape/lib/ap/plan9/execve.c103
-rwxr-xr-xsys/src/ape/lib/ap/plan9/execvp.c24
-rwxr-xr-xsys/src/ape/lib/ap/plan9/fcall.h118
-rwxr-xr-xsys/src/ape/lib/ap/plan9/fcntl.c81
-rwxr-xr-xsys/src/ape/lib/ap/plan9/fork.c19
-rwxr-xr-xsys/src/ape/lib/ap/plan9/frexp.c89
-rwxr-xr-xsys/src/ape/lib/ap/plan9/fstat.c20
-rwxr-xr-xsys/src/ape/lib/ap/plan9/fsync.c10
-rwxr-xr-xsys/src/ape/lib/ap/plan9/ftruncate.c23
-rwxr-xr-xsys/src/ape/lib/ap/plan9/getcwd.c32
-rwxr-xr-xsys/src/ape/lib/ap/plan9/getgid.c21
-rwxr-xr-xsys/src/ape/lib/ap/plan9/getgrgid.c25
-rwxr-xr-xsys/src/ape/lib/ap/plan9/getgrnam.c25
-rwxr-xr-xsys/src/ape/lib/ap/plan9/getgroups.c10
-rwxr-xr-xsys/src/ape/lib/ap/plan9/getlogin.c27
-rwxr-xr-xsys/src/ape/lib/ap/plan9/getpgrp.c27
-rwxr-xr-xsys/src/ape/lib/ap/plan9/getpid.c22
-rwxr-xr-xsys/src/ape/lib/ap/plan9/getppid.c23
-rwxr-xr-xsys/src/ape/lib/ap/plan9/getpwnam.c29
-rwxr-xr-xsys/src/ape/lib/ap/plan9/getpwuid.c30
-rwxr-xr-xsys/src/ape/lib/ap/plan9/getuid.c17
-rwxr-xr-xsys/src/ape/lib/ap/plan9/isatty.c29
-rwxr-xr-xsys/src/ape/lib/ap/plan9/kill.c60
-rwxr-xr-xsys/src/ape/lib/ap/plan9/lib.h72
-rwxr-xr-xsys/src/ape/lib/ap/plan9/link.c12
-rwxr-xr-xsys/src/ape/lib/ap/plan9/lseek.c24
-rwxr-xr-xsys/src/ape/lib/ap/plan9/malloc.c142
-rwxr-xr-xsys/src/ape/lib/ap/plan9/mkdir.c27
-rwxr-xr-xsys/src/ape/lib/ap/plan9/mkfile110
-rwxr-xr-xsys/src/ape/lib/ap/plan9/nan.c54
-rwxr-xr-xsys/src/ape/lib/ap/plan9/open.c62
-rwxr-xr-xsys/src/ape/lib/ap/plan9/opendir.c121
-rwxr-xr-xsys/src/ape/lib/ap/plan9/pause.c11
-rwxr-xr-xsys/src/ape/lib/ap/plan9/pipe.c31
-rwxr-xr-xsys/src/ape/lib/ap/plan9/profile.c324
-rwxr-xr-xsys/src/ape/lib/ap/plan9/qlock.c365
-rwxr-xr-xsys/src/ape/lib/ap/plan9/read.c46
-rwxr-xr-xsys/src/ape/lib/ap/plan9/rename.c76
-rwxr-xr-xsys/src/ape/lib/ap/plan9/rmdir.c17
-rwxr-xr-xsys/src/ape/lib/ap/plan9/setgid.c14
-rwxr-xr-xsys/src/ape/lib/ap/plan9/setpgid.c31
-rwxr-xr-xsys/src/ape/lib/ap/plan9/setsid.c14
-rwxr-xr-xsys/src/ape/lib/ap/plan9/setuid.c14
-rwxr-xr-xsys/src/ape/lib/ap/plan9/signal.c136
-rwxr-xr-xsys/src/ape/lib/ap/plan9/sigpending.c11
-rwxr-xr-xsys/src/ape/lib/ap/plan9/sigprocmask.c23
-rwxr-xr-xsys/src/ape/lib/ap/plan9/sigsuspend.c13
-rwxr-xr-xsys/src/ape/lib/ap/plan9/sleep.c17
-rwxr-xr-xsys/src/ape/lib/ap/plan9/sqrt.c56
-rwxr-xr-xsys/src/ape/lib/ap/plan9/stat.c21
-rwxr-xr-xsys/src/ape/lib/ap/plan9/sys9.h121
-rwxr-xr-xsys/src/ape/lib/ap/plan9/system.c40
-rwxr-xr-xsys/src/ape/lib/ap/plan9/tcgetattr.c201
-rwxr-xr-xsys/src/ape/lib/ap/plan9/time.c27
-rwxr-xr-xsys/src/ape/lib/ap/plan9/times.c51
-rwxr-xr-xsys/src/ape/lib/ap/plan9/tmpfile.c44
-rwxr-xr-xsys/src/ape/lib/ap/plan9/ttyname.c11
-rwxr-xr-xsys/src/ape/lib/ap/plan9/umask.c13
-rwxr-xr-xsys/src/ape/lib/ap/plan9/uname.c25
-rwxr-xr-xsys/src/ape/lib/ap/plan9/unlink.c75
-rwxr-xr-xsys/src/ape/lib/ap/plan9/utime.c30
-rwxr-xr-xsys/src/ape/lib/ap/plan9/wait.c150
-rwxr-xr-xsys/src/ape/lib/ap/plan9/write.c21
105 files changed, 7690 insertions, 0 deletions
diff --git a/sys/src/ape/lib/ap/plan9/9errstr.c b/sys/src/ape/lib/ap/plan9/9errstr.c
new file mode 100755
index 000000000..31226c18b
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/9errstr.c
@@ -0,0 +1,4 @@
+#include "sys9.h"
+
+int
+_ERRSTR( \ No newline at end of file
diff --git a/sys/src/ape/lib/ap/plan9/9iounit.c b/sys/src/ape/lib/ap/plan9/9iounit.c
new file mode 100755
index 000000000..7117a9fe0
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/9iounit.c
@@ -0,0 +1,65 @@
+#include "lib.h"
+#include <string.h>
+#include <stdlib.h>
+#include <fmt.h>
+#include "sys9.h"
+#include "dir.h"
+
+/*
+ * Format:
+ 3 r M 4 (0000000000457def 11 00) 8192 512 /rc/lib/rcmain
+ */
+
+static int
+getfields(char *str, char **args, int max, int mflag)
+{
+ char r;
+ int nr, intok, narg;
+
+ if(max <= 0)
+ return 0;
+
+ narg = 0;
+ args[narg] = str;
+ if(!mflag)
+ narg++;
+ intok = 0;
+ for(;;) {
+ r = *str++;
+ if(r == 0)
+ break;
+ if(r == ' ' || r == '\t'){
+ if(narg >= max)
+ break;
+ *str = 0;
+ intok = 0;
+ args[narg] = str + nr;
+ if(!mflag)
+ narg++;
+ } else {
+ if(!intok && mflag)
+ narg++;
+ intok = 1;
+ }
+ }
+ return narg;
+}
+int
+_IOUNIT(int fd)
+{
+ int i, cfd;
+ char buf[128], *args[10];
+
+ snprint(buf, sizeof buf, "#d/%dctl", fd);
+ cfd = _OPEN(buf, OREAD);
+ if(cfd < 0)
+ return 0;
+ i = _READ(cfd, buf, sizeof buf-1);
+ _CLOSE(cfd);
+ if(i <= 0)
+ return 0;
+ buf[i] = '\0';
+ if(getfields(buf, args, 10, 1) != 10)
+ return 0;
+ return atoi(args[7]);
+}
diff --git a/sys/src/ape/lib/ap/plan9/9mallocz.c b/sys/src/ape/lib/ap/plan9/9mallocz.c
new file mode 100755
index 000000000..de5edb266
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/9mallocz.c
@@ -0,0 +1,14 @@
+#include <stdlib.h>
+#include <string.h>
+
+void*
+_MALLOCZ(int n, int clr)
+{
+ void *v;
+
+ v = malloc(n);
+ if(v && clr)
+ memset(v, 0, n);
+ return v;
+}
+
diff --git a/sys/src/ape/lib/ap/plan9/9read.c b/sys/src/ape/lib/ap/plan9/9read.c
new file mode 100755
index 000000000..ce38b3908
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/9read.c
@@ -0,0 +1,11 @@
+#include "lib.h"
+#include <string.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include "dir.h"
+
+long
+_READ(int fd, void *buf, long n)
+{
+ return _PREAD(fd, buf, n, -1LL);
+}
diff --git a/sys/src/ape/lib/ap/plan9/9readn.c b/sys/src/ape/lib/ap/plan9/9readn.c
new file mode 100755
index 000000000..8a0f5615c
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/9readn.c
@@ -0,0 +1,21 @@
+#include "sys9.h"
+
+long
+_READN(int f, void *av, long n)
+{
+ char *a;
+ long m, t;
+
+ a = av;
+ t = 0;
+ while(t < n){
+ m = _READ(f, a+t, n-t);
+ if(m <= 0){
+ if(t == 0)
+ return m;
+ break;
+ }
+ t += m;
+ }
+ return t;
+}
diff --git a/sys/src/ape/lib/ap/plan9/9wait.c b/sys/src/ape/lib/ap/plan9/9wait.c
new file mode 100755
index 000000000..71771df0b
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/9wait.c
@@ -0,0 +1,93 @@
+#include "lib.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include "dir.h"
+
+static char qsep[] = " \t\r\n";
+
+static char*
+qtoken(char *s)
+{
+ int quoting;
+ char *t;
+
+ quoting = 0;
+ t = s; /* s is output string, t is input string */
+ while(*t!='\0' && (quoting || strchr(qsep, *t)==nil)){
+ if(*t != '\''){
+ *s++ = *t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting){
+ quoting = 1;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '\''){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ quoting = 0;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t++;
+ *s++ = *t++;
+ }
+ if(*s != '\0'){
+ *s = '\0';
+ if(t == s)
+ t++;
+ }
+ return t;
+}
+
+static int
+tokenize(char *s, char **args, int maxargs)
+{
+ int nargs;
+
+ for(nargs=0; nargs<maxargs; nargs++){
+ while(*s!='\0' && strchr(qsep, *s)!=nil)
+ s++;
+ if(*s == '\0')
+ break;
+ args[nargs] = s;
+ s = qtoken(s);
+ }
+
+ return nargs;
+}
+
+Waitmsg*
+_WAIT(void)
+{
+ int n, l;
+ char buf[512], *fld[5];
+ Waitmsg *w;
+
+ n = _AWAIT(buf, sizeof buf-1);
+ if(n < 0)
+ return nil;
+ buf[n] = '\0';
+ if(tokenize(buf, fld, 5) != 5){
+ strcpy(buf, "couldn't parse wait message");
+ _ERRSTR(buf, sizeof buf);
+ return nil;
+ }
+ l = strlen(fld[4])+1;
+ w = malloc(sizeof(Waitmsg)+l);
+ if(w == nil)
+ return nil;
+ w->pid = atoi(fld[0]);
+ w->time[0] = atoi(fld[1]);
+ w->time[1] = atoi(fld[2]);
+ w->time[2] = atoi(fld[3]);
+ w->msg = (char*)&w[1];
+ memmove(w->msg, fld[4], l);
+ return w;
+}
+
diff --git a/sys/src/ape/lib/ap/plan9/9write.c b/sys/src/ape/lib/ap/plan9/9write.c
new file mode 100755
index 000000000..e87a45ade
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/9write.c
@@ -0,0 +1,11 @@
+#include "lib.h"
+#include <string.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include "dir.h"
+
+long
+_WRITE(int fd, void *buf, long n)
+{
+ return _PWRITE(fd, buf, n, -1LL);
+}
diff --git a/sys/src/ape/lib/ap/plan9/_buf.c b/sys/src/ape/lib/ap/plan9/_buf.c
new file mode 100755
index 000000000..21fe282bb
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/_buf.c
@@ -0,0 +1,472 @@
+#define _BSDTIME_EXTENSION
+#define _LOCK_EXTENSION
+#include "lib.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <lock.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <unistd.h>
+#include "sys9.h"
+
+typedef struct Muxseg {
+ Lock lock; /* for mutual exclusion access to buffer variables */
+ int curfds; /* number of fds currently buffered */
+ int selwait; /* true if selecting process is waiting */
+ int waittime; /* time for timer process to wait */
+ fd_set rwant; /* fd's that select wants to read */
+ fd_set ewant; /* fd's that select wants to know eof info on */
+ Muxbuf bufs[INITBUFS]; /* can grow, via segbrk() */
+} Muxseg;
+
+#define MUXADDR ((void*)0x6000000)
+static Muxseg *mux = 0; /* shared memory segment */
+
+/* _muxsid and _killmuxsid are known in libbsd's listen.c */
+int _muxsid = -1; /* group id of copy processes */
+static int _mainpid = -1;
+static int timerpid = -1; /* pid of a timer process */
+
+void _killmuxsid(void);
+static void _copyproc(int, Muxbuf*);
+static void _timerproc(void);
+static void _resettimer(void);
+
+static int copynotehandler(void *, char *);
+
+/* assume FD_SETSIZE is 96 */
+#define FD_ANYSET(p) ((p)->fds_bits[0] || (p)->fds_bits[1] || (p)->fds_bits[2])
+
+/*
+ * Start making fd read-buffered: make the shared segment, if necessary,
+ * allocate a slot (index into mux->bufs), and fork a child to read the fd
+ * and write into the slot-indexed buffer.
+ * Return -1 if we can't do it.
+ */
+int
+_startbuf(int fd)
+{
+ long i, n, slot;
+ int pid, sid;
+ Fdinfo *f;
+ Muxbuf *b;
+
+ if(mux == 0){
+ _RFORK(RFREND);
+ mux = (Muxseg*)_SEGATTACH(0, "shared", MUXADDR, sizeof(Muxseg));
+ if((long)mux == -1){
+ _syserrno();
+ return -1;
+ }
+ /* segattach has returned zeroed memory */
+ atexit(_killmuxsid);
+ }
+
+ if(fd == -1)
+ return 0;
+
+ lock(&mux->lock);
+ slot = mux->curfds++;
+ if(mux->curfds > INITBUFS) {
+ if(_SEGBRK(mux, mux->bufs+mux->curfds) < 0){
+ _syserrno();
+ unlock(&mux->lock);
+ return -1;
+ }
+ }
+
+ f = &_fdinfo[fd];
+ b = &mux->bufs[slot];
+ b->n = 0;
+ b->putnext = b->data;
+ b->getnext = b->data;
+ b->eof = 0;
+ b->fd = fd;
+ if(_mainpid == -1)
+ _mainpid = getpid();
+ if((pid = _RFORK(RFFDG|RFPROC|RFNOWAIT)) == 0){
+ /* copy process ... */
+ if(_muxsid == -1) {
+ _RFORK(RFNOTEG);
+ _muxsid = getpgrp();
+ } else
+ setpgid(getpid(), _muxsid);
+ _NOTIFY(copynotehandler);
+ for(i=0; i<OPEN_MAX; i++)
+ if(i!=fd && (_fdinfo[i].flags&FD_ISOPEN))
+ _CLOSE(i);
+ _RENDEZVOUS(0, _muxsid);
+ _copyproc(fd, b);
+ }
+
+ /* parent process continues ... */
+ b->copypid = pid;
+ f->buf = b;
+ f->flags |= FD_BUFFERED;
+ unlock(&mux->lock);
+ _muxsid = _RENDEZVOUS(0, 0);
+ /* leave fd open in parent so system doesn't reuse it */
+ return 0;
+}
+
+/*
+ * The given buffered fd is being closed.
+ * Set the fd field in the shared buffer to -1 to tell copyproc
+ * to exit, and kill the copyproc.
+ */
+void
+_closebuf(int fd)
+{
+ Muxbuf *b;
+
+ b = _fdinfo[fd].buf;
+ if(!b)
+ return;
+ lock(&mux->lock);
+ b->fd = -1;
+ unlock(&mux->lock);
+ kill(b->copypid, SIGKILL);
+}
+
+/* child copy procs execute this until eof */
+static void
+_copyproc(int fd, Muxbuf *b)
+{
+ unsigned char *e;
+ int n;
+ int nzeros;
+
+ e = &b->data[PERFDMAX];
+ for(;;) {
+ /* make sure there's room */
+ lock(&mux->lock);
+ if(e - b->putnext < READMAX) {
+ if(b->getnext == b->putnext) {
+ b->getnext = b->putnext = b->data;
+ unlock(&mux->lock);
+ } else {
+ /* sleep until there's room */
+ b->roomwait = 1;
+ unlock(&mux->lock);
+ _RENDEZVOUS((unsigned long)&b->roomwait, 0);
+ }
+ } else
+ unlock(&mux->lock);
+ /*
+ * A Zero-length _READ might mean a zero-length write
+ * happened, or it might mean eof; try several times to
+ * disambiguate (posix read() discards 0-length messages)
+ */
+ nzeros = 0;
+ do {
+ n = _READ(fd, b->putnext, READMAX);
+ if(b->fd == -1) {
+ _exit(0); /* we've been closed */
+ }
+ } while(n == 0 && ++nzeros < 3);
+ lock(&mux->lock);
+ if(n <= 0) {
+ b->eof = 1;
+ if(mux->selwait && FD_ISSET(fd, &mux->ewant)) {
+ mux->selwait = 0;
+ unlock(&mux->lock);
+ _RENDEZVOUS((unsigned long)&mux->selwait, fd);
+ } else if(b->datawait) {
+ b->datawait = 0;
+ unlock(&mux->lock);
+ _RENDEZVOUS((unsigned long)&b->datawait, 0);
+ } else if(mux->selwait && FD_ISSET(fd, &mux->rwant)) {
+ mux->selwait = 0;
+ unlock(&mux->lock);
+ _RENDEZVOUS((unsigned long)&mux->selwait, fd);
+ } else
+ unlock(&mux->lock);
+ _exit(0);
+ } else {
+ b->putnext += n;
+ b->n += n;
+ if(b->n > 0) {
+ /* parent process cannot be both in datawait and selwait */
+ if(b->datawait) {
+ b->datawait = 0;
+ unlock(&mux->lock);
+ /* wake up _bufreading process */
+ _RENDEZVOUS((unsigned long)&b->datawait, 0);
+ } else if(mux->selwait && FD_ISSET(fd, &mux->rwant)) {
+ mux->selwait = 0;
+ unlock(&mux->lock);
+ /* wake up selecting process */
+ _RENDEZVOUS((unsigned long)&mux->selwait, fd);
+ } else
+ unlock(&mux->lock);
+ } else
+ unlock(&mux->lock);
+ }
+ }
+}
+
+/* like read(), for a buffered fd; extra arg noblock says don't wait for data if true */
+int
+_readbuf(int fd, void *addr, int nwant, int noblock)
+{
+ Muxbuf *b;
+ int ngot;
+
+ b = _fdinfo[fd].buf;
+ if(b->eof && b->n == 0) {
+goteof:
+ return 0;
+ }
+ if(b->n == 0 && noblock) {
+ errno = EAGAIN;
+ return -1;
+ }
+ /* make sure there's data */
+ lock(&mux->lock);
+ ngot = b->putnext - b->getnext;
+ if(ngot == 0) {
+ /* maybe EOF just happened */
+ if(b->eof) {
+ unlock(&mux->lock);
+ goto goteof;
+ }
+ /* sleep until there's data */
+ b->datawait = 1;
+ unlock(&mux->lock);
+ _RENDEZVOUS((unsigned long)&b->datawait, 0);
+ lock(&mux->lock);
+ ngot = b->putnext - b->getnext;
+ }
+ if(ngot == 0) {
+ unlock(&mux->lock);
+ goto goteof;
+ }
+ if(ngot > nwant)
+ ngot = nwant;
+ memcpy(addr, b->getnext, ngot);
+ b->getnext += ngot;
+ b->n -= ngot;
+ if(b->getnext == b->putnext && b->roomwait) {
+ b->getnext = b->putnext = b->data;
+ b->roomwait = 0;
+ unlock(&mux->lock);
+ /* wake up copy process */
+ _RENDEZVOUS((unsigned long)&b->roomwait, 0);
+ } else
+ unlock(&mux->lock);
+ return ngot;
+}
+
+int
+select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout)
+{
+ int n, i, tmp, t, slots, fd, err;
+ Fdinfo *f;
+ Muxbuf *b;
+
+ if(timeout)
+ t = timeout->tv_sec*1000 + (timeout->tv_usec+999)/1000;
+ else
+ t = -1;
+ if(!((rfds && FD_ANYSET(rfds)) || (wfds && FD_ANYSET(wfds))
+ || (efds && FD_ANYSET(efds)))) {
+ /* no requested fds */
+ if(t > 0)
+ _SLEEP(t);
+ return 0;
+ }
+
+ _startbuf(-1);
+
+ /* make sure all requested rfds and efds are buffered */
+ if(nfds >= OPEN_MAX)
+ nfds = OPEN_MAX;
+ for(i = 0; i < nfds; i++)
+ if((rfds && FD_ISSET(i, rfds)) || (efds && FD_ISSET(i, efds))){
+ f = &_fdinfo[i];
+ if(!(f->flags&FD_BUFFERED))
+ if(_startbuf(i) != 0) {
+ return -1;
+ }
+ b = f->buf;
+ if(rfds && FD_ISSET(i,rfds) && b->eof && b->n == 0)
+ if(efds == 0 || !FD_ISSET(i,efds)) {
+ errno = EBADF; /* how X tells a client is gone */
+ return -1;
+ }
+ }
+
+ /* check wfds; for now, we'll say they are all ready */
+ n = 0;
+ if(wfds && FD_ANYSET(wfds)){
+ for(i = 0; i<nfds; i++)
+ if(FD_ISSET(i, wfds)) {
+ n++;
+ }
+ }
+
+ lock(&mux->lock);
+
+ slots = mux->curfds;
+ FD_ZERO(&mux->rwant);
+ FD_ZERO(&mux->ewant);
+
+ for(i = 0; i<slots; i++) {
+ b = &mux->bufs[i];
+ fd = b->fd;
+ if(fd == -1)
+ continue;
+ err = 0;
+ if(efds && FD_ISSET(fd, efds)) {
+ if(b->eof && b->n == 0){
+ err = 1;
+ n++;
+ }else{
+ FD_CLR(fd, efds);
+ FD_SET(fd, &mux->ewant);
+ }
+ }
+ if(rfds && FD_ISSET(fd, rfds)) {
+ if(!err && (b->n > 0 || b->eof))
+ n++;
+ else{
+ FD_CLR(fd, rfds);
+ FD_SET(fd, &mux->rwant);
+ }
+ }
+ }
+ if(n || !(FD_ANYSET(&mux->rwant) || FD_ANYSET(&mux->ewant)) || t == 0) {
+ FD_ZERO(&mux->rwant);
+ FD_ZERO(&mux->ewant);
+ unlock(&mux->lock);
+ return n;
+ }
+
+ if(timeout) {
+ mux->waittime = t;
+ if(timerpid == -1)
+ _timerproc();
+ else
+ _resettimer();
+ }
+ mux->selwait = 1;
+ unlock(&mux->lock);
+ fd = _RENDEZVOUS((unsigned long)&mux->selwait, 0);
+ if(fd >= 0) {
+ b = _fdinfo[fd].buf;
+ if(FD_ISSET(fd, &mux->rwant)) {
+ FD_SET(fd, rfds);
+ n = 1;
+ } else if(FD_ISSET(fd, &mux->ewant) && b->eof && b->n == 0) {
+ FD_SET(fd, efds);
+ n = 1;
+ }
+ }
+ FD_ZERO(&mux->rwant);
+ FD_ZERO(&mux->ewant);
+ return n;
+}
+
+static int timerreset;
+static int timerpid;
+
+static void
+alarmed(int v)
+{
+ timerreset = 1;
+}
+
+/* a little over an hour */
+#define LONGWAIT 4000001
+
+static void
+_killtimerproc(void)
+{
+ if(timerpid > 0)
+ kill(timerpid, SIGKILL);
+}
+
+static void
+_timerproc(void)
+{
+ int i;
+
+ if((timerpid = _RFORK(RFFDG|RFPROC|RFNOWAIT)) == 0){
+ /* timer process */
+ setpgid(getpid(), _muxsid);
+ signal(SIGALRM, alarmed);
+ for(i=0; i<OPEN_MAX; i++)
+ _CLOSE(i);
+ _RENDEZVOUS(1, 0);
+ for(;;) {
+ _SLEEP(mux->waittime);
+ if(timerreset) {
+ timerreset = 0;
+ } else {
+ lock(&mux->lock);
+ if(mux->selwait && mux->waittime != LONGWAIT) {
+ mux->selwait = 0;
+ mux->waittime = LONGWAIT;
+ unlock(&mux->lock);
+ _RENDEZVOUS((unsigned long)&mux->selwait, -2);
+ } else {
+ mux->waittime = LONGWAIT;
+ unlock(&mux->lock);
+ }
+ }
+ }
+ }
+ atexit(_killtimerproc);
+ /* parent process continues */
+ _RENDEZVOUS(1, 0);
+}
+
+static void
+_resettimer(void)
+{
+ kill(timerpid, SIGALRM);
+}
+
+void
+_killmuxsid(void)
+{
+ if(_muxsid != -1 && (_mainpid == getpid() || _mainpid == -1))
+ kill(-_muxsid,SIGTERM);
+}
+
+/* call this on fork(), because reading a BUFFERED fd won't work in child */
+void
+_detachbuf(void)
+{
+ int i;
+ Fdinfo *f;
+
+ if(mux == 0)
+ return;
+ _SEGDETACH(mux);
+ for(i = 0; i < OPEN_MAX; i++){
+ f = &_fdinfo[i];
+ if(f->flags&FD_BUFFERED)
+ f->flags = (f->flags&~FD_BUFFERED) | FD_BUFFEREDX;
+ /* mark 'poisoned' */
+ }
+ mux = 0;
+ _muxsid = -1;
+ _mainpid = -1;
+ timerpid = -1;
+}
+
+static int
+copynotehandler(void *u, char *msg)
+{
+ int i;
+ void(*f)(int);
+
+ if(_finishing)
+ _finish(0, 0);
+ _NOTED(1);
+}
diff --git a/sys/src/ape/lib/ap/plan9/_dirconv.c b/sys/src/ape/lib/ap/plan9/_dirconv.c
new file mode 100755
index 000000000..3889c2ee2
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/_dirconv.c
@@ -0,0 +1,65 @@
+#include "lib.h"
+#include <string.h>
+#include "sys9.h"
+#include "dir.h"
+
+#define CHAR(x) *p++ = f->x
+#define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2
+#define LONG(x) p[0] = f->x; p[1] = f->x>>8; p[2] = f->x>>16; p[3] = f->x>>24; p += 4
+#define VLONG(x) p[0] = f->x; p[1] = f->x>>8; p[2] = f->x>>16; p[3] = f->x>>24;\
+ p[4] = 0; p[5] = 0; p[6] = 0; p[7] = 0; p += 8
+#define STRING(x,n) memcpy(p, f->x, n); p += n
+
+int
+convD2M(Dir *f, char *ap)
+{
+ unsigned char *p;
+
+ p = (unsigned char*)ap;
+ STRING(name, sizeof(f->name));
+ STRING(uid, sizeof(f->uid));
+ STRING(gid, sizeof(f->gid));
+ LONG(qid.path);
+ LONG(qid.vers);
+ LONG(mode);
+ LONG(atime);
+ LONG(mtime);
+ VLONG(length);
+ SHORT(type);
+ SHORT(dev);
+ return p - (unsigned char*)ap;
+}
+
+#undef CHAR
+#undef SHORT
+#undef LONG
+#undef VLONG
+#undef STRING
+
+#define CHAR(x) f->x = *p++
+#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2
+#define LONG(x) f->x = (p[0] | (p[1]<<8) |\
+ (p[2]<<16) | (p[3]<<24)); p += 4
+#define VLONG(x) f->x = (p[0] | (p[1]<<8) |\
+ (p[2]<<16) | (p[3]<<24)); p += 8
+#define STRING(x,n) memcpy(f->x, p, n); p += n
+
+int
+convM2D(char *ap, Dir *f)
+{
+ unsigned char *p;
+
+ p = (unsigned char*)ap;
+ STRING(name, sizeof(f->name));
+ STRING(uid, sizeof(f->uid));
+ STRING(gid, sizeof(f->gid));
+ LONG(qid.path);
+ LONG(qid.vers);
+ LONG(mode);
+ LONG(atime);
+ LONG(mtime);
+ VLONG(length);
+ SHORT(type);
+ SHORT(dev);
+ return p - (unsigned char*)ap;
+}
diff --git a/sys/src/ape/lib/ap/plan9/_envsetup.c b/sys/src/ape/lib/ap/plan9/_envsetup.c
new file mode 100755
index 000000000..55cf33b85
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/_envsetup.c
@@ -0,0 +1,123 @@
+#include "lib.h"
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include "sys9.h"
+#include "dir.h"
+
+/*
+ * Called before main to initialize environ.
+ * Some plan9 environment variables
+ * have 0 bytes in them (notably $path);
+ * we change them to 1's (and execve changes back)
+ *
+ * Also, register the note handler.
+ */
+
+char **environ;
+int errno;
+unsigned long _clock;
+static char name[NAME_MAX+5] = "#e";
+static void fdsetup(char *, char *);
+static void sigsetup(char *, char *);
+
+enum {
+ Envhunk=7000,
+};
+
+void
+_envsetup(void)
+{
+ int dfd;
+ struct dirent *de;
+ int n, nd, m, i, j, f;
+ int psize, cnt;
+ int nohandle;
+ int fdinited;
+ char *ps, *p;
+ char **pp;
+ Dir *d9, *d9a;
+
+ nohandle = 0;
+ fdinited = 0;
+ cnt = 0;
+ dfd = _OPEN(name, 0);
+ if(dfd < 0) {
+ static char **emptyenvp = 0;
+ environ = emptyenvp;
+ return;
+ }
+ name[2] = '/';
+ ps = p = malloc(Envhunk);
+ psize = Envhunk;
+ nd = _dirreadall(dfd, &d9a);
+ _CLOSE(dfd);
+ for(j=0; j<nd; j++){
+ d9 = &d9a[j];
+ n = strlen(d9->name);
+ m = d9->length;
+ i = p - ps;
+ if(i+n+1+m+1 > psize) {
+ psize += (n+m+2 < Envhunk)? Envhunk : n+m+2;
+ ps = realloc(ps, psize);
+ p = ps + i;
+ }
+ memcpy(p, d9->name, n);
+ p[n] = '=';
+ strcpy(name+3, d9->name);
+ f = _OPEN(name, O_RDONLY);
+ if(f < 0 || _READ(f, p+n+1, m) != m)
+ m = 0;
+ _CLOSE(f);
+ if(p[n+m] == 0)
+ m--;
+ for(i=0; i<m; i++)
+ if(p[n+1+i] == 0)
+ p[n+1+i] = 1;
+ p[n+1+m] = 0;
+ if(strcmp(d9->name, "_fdinfo") == 0) {
+ _fdinit(p+n+1, p+n+1+m);
+ fdinited = 1;
+ } else if(strcmp(d9->name, "_sighdlr") == 0)
+ sigsetup(p+n+1, p+n+1+m);
+ else if(strcmp(d9->name, "nohandle") == 0)
+ nohandle = 1;
+ p += n+m+2;
+ cnt++;
+ }
+ free(d9a);
+ if(!fdinited)
+ _fdinit(0, 0);
+ environ = pp = malloc((1+cnt)*sizeof(char *));
+ p = ps;
+ for(i = 0; i < cnt; i++) {
+ *pp++ = p;
+ p = memchr(p, 0, ps+psize-p);
+ if (!p)
+ break;
+ p++;
+ }
+ *pp = 0;
+ if(!nohandle)
+ _NOTIFY(_notehandler);
+}
+
+static void
+sigsetup(char *s, char *se)
+{
+ int i, sig;
+ char *e;
+
+ while(s < se){
+ sig = strtoul(s, &e, 10);
+ if(s == e)
+ break;
+ s = e;
+ if(sig <= MAXSIG)
+ _sighdlr[sig] = SIG_IGN;
+ }
+}
diff --git a/sys/src/ape/lib/ap/plan9/_errno.c b/sys/src/ape/lib/ap/plan9/_errno.c
new file mode 100755
index 000000000..8e5caad7d
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/_errno.c
@@ -0,0 +1,124 @@
+#include "lib.h"
+#include "sys9.h"
+#include <string.h>
+#include <errno.h>
+
+/* make this global, so programs can look at it if errno is unilluminating */
+
+/* see also: ../stdio/strerror.c, with errno-> string mapping */
+
+char _plan9err[ERRMAX];
+
+static struct errmap {
+ int errno;
+ char *ename;
+} map[] = {
+ /* from /sys/src/9/port/errstr.h */
+ {EINVAL, "inconsistent mount"},
+ {EINVAL, "not mounted"},
+ {EINVAL, "not in union"},
+ {EIO, "mount rpc error"},
+ {EIO, "mounted device shut down"},
+ {EPERM, "mounted directory forbids creation"},
+ {ENOENT, "does not exist"},
+ {ENXIO, "unknown device in # filename"},
+ {ENOTDIR, "not a directory"},
+ {EISDIR, "file is a directory"},
+ {EINVAL, "bad character in file name"},
+ {EINVAL, "file name syntax"},
+ {EPERM, "permission denied"},
+ {EPERM, "inappropriate use of fd"},
+ {EINVAL, "bad arg in system call"},
+ {EBUSY, "device or object already in use"},
+ {EIO, "i/o error"},
+ {EIO, "read or write too large"},
+ {EIO, "read or write too small"},
+ {EADDRINUSE, "network port not available"},
+ {ESHUTDOWN, "write to hungup stream"},
+ {ESHUTDOWN, "i/o on hungup channel"},
+ {EINVAL, "bad process or channel control request"},
+ {EBUSY, "no free devices"},
+ {ESRCH, "process exited"},
+ {ECHILD, "no living children"},
+ {EIO, "i/o error in demand load"},
+ {ENOMEM, "virtual memory allocation failed"},
+ {EBADF, "fd out of range or not open"},
+ {EMFILE, "no free file descriptors"},
+ {ESPIPE, "seek on a stream"},
+ {ENOEXEC, "exec header invalid"},
+ {ETIMEDOUT, "connection timed out"},
+ {ECONNREFUSED, "connection refused"},
+ {ECONNREFUSED, "connection in use"},
+ {EINTR, "interrupted"},
+ {ENOMEM, "kernel allocate failed"},
+ {EINVAL, "segments overlap"},
+ {EIO, "i/o count too small"},
+ {EGREG, "ken has left the building"},
+ {EINVAL, "bad attach specifier"},
+
+ /* from exhausted() calls in kernel */
+ {ENFILE, "no free file descriptors"},
+ {EBUSY, "no free mount devices"},
+ {EBUSY, "no free mount rpc buffer"},
+ {EBUSY, "no free segments"},
+ {ENOMEM, "no free memory"},
+ {ENOBUFS, "no free Blocks"},
+ {EBUSY, "no free routes"},
+
+ /* from ken */
+ {EINVAL, "attach -- bad specifier"},
+ {EBADF, "unknown fid"},
+ {EINVAL, "bad character in directory name"},
+ {EBADF, "read/write -- on non open fid"},
+ {EIO, "read/write -- count too big"},
+ {EIO, "phase error -- directory entry not allocated"},
+ {EIO, "phase error -- qid does not match"},
+ {EACCES, "access permission denied"},
+ {ENOENT, "directory entry not found"},
+ {EINVAL, "open/create -- unknown mode"},
+ {ENOTDIR, "walk -- in a non-directory"},
+ {ENOTDIR, "create -- in a non-directory"},
+ {EIO, "phase error -- cannot happen"},
+ {EEXIST, "create -- file exists"},
+ {EINVAL, "create -- . and .. illegal names"},
+ {ENOTEMPTY, "directory not empty"},
+ {EINVAL, "attach -- privileged user"},
+ {EPERM, "wstat -- not owner"},
+ {EPERM, "wstat -- not in group"},
+ {EINVAL, "create/wstat -- bad character in file name"},
+ {EBUSY, "walk -- too many (system wide)"},
+ {EROFS, "file system read only"},
+ {ENOSPC, "file system full"},
+ {EINVAL, "read/write -- offset negative"},
+ {EBUSY, "open/create -- file is locked"},
+ {EBUSY, "close/read/write -- lock is broken"},
+
+ /* from sockets */
+ {ENOTSOCK, "not a socket"},
+ {EPROTONOSUPPORT, "protocol not supported"},
+ {ECONNREFUSED, "connection refused"},
+ {EAFNOSUPPORT, "address family not supported"},
+ {ENOBUFS, "insufficient buffer space"},
+ {EOPNOTSUPP, "operation not supported"},
+ {EADDRINUSE, "address in use"},
+ {EGREG, "unnamed error message"},
+};
+
+#define NERRMAP (sizeof(map)/sizeof(struct errmap))
+
+/* convert last system call error to an errno */
+void
+_syserrno(void)
+{
+ int i;
+
+ if(_ERRSTR(_plan9err, sizeof _plan9err) < 0)
+ errno = EINVAL;
+ else{
+ for(i = 0; i < NERRMAP; i++)
+ if(strstr(_plan9err, map[i].ename) != 0)
+ break;
+ _ERRSTR(_plan9err, sizeof _plan9err);
+ errno = (i < NERRMAP)? map[i].errno : EINVAL;
+ }
+}
diff --git a/sys/src/ape/lib/ap/plan9/_exit.c b/sys/src/ape/lib/ap/plan9/_exit.c
new file mode 100755
index 000000000..e3886ff18
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/_exit.c
@@ -0,0 +1,58 @@
+#include "lib.h"
+#include "sys9.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+int _finishing = 0;
+int _sessleader = 0;
+
+static char exitstatus[ERRMAX];
+
+void
+_exit(int status)
+{
+ _finish(status, 0);
+}
+
+void
+_finish(int status, char *term)
+{
+ int i, nalive;
+ char *cp;
+
+ if(_finishing)
+ _EXITS(exitstatus);
+ _finishing = 1;
+ if(status){
+ cp = _ultoa(exitstatus, status & 0xFF);
+ *cp = 0;
+ }else if(term){
+ strncpy(exitstatus, term, ERRMAX);
+ exitstatus[ERRMAX-1] = '\0';
+ }
+ if(_sessleader)
+ kill(0, SIGTERM);
+ _EXITS(exitstatus);
+}
+
+/* emulate: return p+sprintf(p, "%uld", v) */
+#define IDIGIT 15
+char *
+_ultoa(char *p, unsigned long v)
+{
+ char s[IDIGIT];
+ int n, i;
+
+ s[IDIGIT-1] = 0;
+ for(i = IDIGIT-2; i; i--){
+ n = v % 10;
+ s[i] = n + '0';
+ v = v / 10;
+ if(v == 0)
+ break;
+ }
+ strcpy(p, s+i);
+ return p + (IDIGIT-1-i);
+}
diff --git a/sys/src/ape/lib/ap/plan9/_fcall.c b/sys/src/ape/lib/ap/plan9/_fcall.c
new file mode 100755
index 000000000..394f4cc08
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/_fcall.c
@@ -0,0 +1,422 @@
+#include <string.h>
+#include "sys9.h"
+#include "lib.h"
+#include "dir.h"
+#include "fcall.h"
+
+typedef unsigned char uchar;
+
+#define CHAR(x) *p++ = f->x
+#define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2
+#define LONG(x) p[0] = f->x; p[1] = f->x>>8; p[2] = f->x>>16; p[3] = f->x>>24; p += 4
+#define VLONG(x) p[0] = f->x; p[1] = f->x>>8; p[2] = f->x>>16; p[3] = f->x>>24;\
+ p[4] = 0; p[5] = 0; p[6] = 0; p[7] = 0; p += 8
+#define STRING(x,n) memcpy(p, f->x, n); p += n
+
+int
+convS2M(Fcall *f, char *ap)
+{
+ uchar *p;
+
+ p = (uchar*)ap;
+ CHAR(type);
+ SHORT(tag);
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tosession:
+ case Tnop:
+ break;
+
+ case Tsession:
+ STRING(chal, sizeof(f->chal));
+ break;
+
+ case Tflush:
+ SHORT(oldtag);
+ break;
+
+ case Tattach:
+ SHORT(fid);
+ STRING(uname, sizeof(f->uname));
+ STRING(aname, sizeof(f->aname));
+ STRING(ticket, sizeof(f->ticket));
+ STRING(auth, sizeof(f->auth));
+ break;
+
+ case Toattach:
+ SHORT(fid);
+ STRING(uname, sizeof(f->uname));
+ STRING(aname, sizeof(f->aname));
+ STRING(ticket, NAMELEN);
+ break;
+
+ case Tauth:
+ SHORT(fid);
+ STRING(uname, sizeof(f->uname));
+ STRING(ticket, 8+NAMELEN);
+ break;
+
+ case Tclone:
+ SHORT(fid);
+ SHORT(newfid);
+ break;
+
+ case Twalk:
+ SHORT(fid);
+ STRING(name, sizeof(f->name));
+ break;
+
+ case Topen:
+ SHORT(fid);
+ CHAR(mode);
+ break;
+
+ case Tcreate:
+ SHORT(fid);
+ STRING(name, sizeof(f->name));
+ LONG(perm);
+ CHAR(mode);
+ break;
+
+ case Tread:
+ SHORT(fid);
+ VLONG(offset);
+ SHORT(count);
+ break;
+
+ case Twrite:
+ SHORT(fid);
+ VLONG(offset);
+ SHORT(count);
+ p++; /* pad(1) */
+ STRING(data, f->count);
+ break;
+
+ case Tclunk:
+ SHORT(fid);
+ break;
+
+ case Tremove:
+ SHORT(fid);
+ break;
+
+ case Tstat:
+ SHORT(fid);
+ break;
+
+ case Twstat:
+ SHORT(fid);
+ STRING(stat, sizeof(f->stat));
+ break;
+
+ case Tclwalk:
+ SHORT(fid);
+ SHORT(newfid);
+ STRING(name, sizeof(f->name));
+ break;
+/*
+ */
+ case Rosession:
+ case Rnop:
+ break;
+
+ case Rsession:
+ STRING(chal, sizeof(f->chal));
+ STRING(authid, sizeof(f->authid));
+ STRING(authdom, sizeof(f->authdom));
+ break;
+
+ case Rerror:
+ STRING(ename, sizeof(f->ename));
+ break;
+
+ case Rflush:
+ break;
+
+ case Rattach:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ STRING(rauth, sizeof(f->rauth));
+ break;
+
+ case Roattach:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ break;
+
+ case Rauth:
+ SHORT(fid);
+ STRING(ticket, 8+8+7+7);
+ break;
+
+ case Rclone:
+ SHORT(fid);
+ break;
+
+ case Rwalk:
+ case Rclwalk:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ break;
+
+ case Ropen:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ break;
+
+ case Rcreate:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ break;
+
+ case Rread:
+ SHORT(fid);
+ SHORT(count);
+ p++; /* pad(1) */
+ STRING(data, f->count);
+ break;
+
+ case Rwrite:
+ SHORT(fid);
+ SHORT(count);
+ break;
+
+ case Rclunk:
+ SHORT(fid);
+ break;
+
+ case Rremove:
+ SHORT(fid);
+ break;
+
+ case Rstat:
+ SHORT(fid);
+ STRING(stat, sizeof(f->stat));
+ break;
+
+ case Rwstat:
+ SHORT(fid);
+ break;
+ }
+ return p - (uchar*)ap;
+}
+
+#undef CHAR
+#undef SHORT
+#undef LONG
+#undef VLONG
+#undef STRING
+
+#define CHAR(x) f->x = *p++
+#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2
+#define LONG(x) f->x = (p[0] | (p[1]<<8) |\
+ (p[2]<<16) | (p[3]<<24)); p += 4
+#define VLONG(x) f->x = (p[0] | (p[1]<<8) |\
+ (p[2]<<16) | (p[3]<<24)); p += 8
+#define STRING(x,n) memcpy(f->x, p, n); p += n
+
+int
+convM2S(char *ap, Fcall *f, int n)
+{
+ uchar *p;
+
+ p = (uchar*)ap;
+ CHAR(type);
+ SHORT(tag);
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tnop:
+ case Tosession:
+ break;
+
+ case Tsession:
+ STRING(chal, sizeof(f->chal));
+ break;
+
+ case Tflush:
+ SHORT(oldtag);
+ break;
+
+ case Tattach:
+ SHORT(fid);
+ STRING(uname, sizeof(f->uname));
+ STRING(aname, sizeof(f->aname));
+ STRING(ticket, sizeof(f->ticket));
+ STRING(auth, sizeof(f->auth));
+ break;
+
+ case Toattach:
+ SHORT(fid);
+ STRING(uname, sizeof(f->uname));
+ STRING(aname, sizeof(f->aname));
+ STRING(ticket, NAMELEN);
+ break;
+
+ case Tauth:
+ SHORT(fid);
+ STRING(uname, sizeof(f->uname));
+ STRING(ticket, 8+NAMELEN);
+ break;
+
+ case Tclone:
+ SHORT(fid);
+ SHORT(newfid);
+ break;
+
+ case Twalk:
+ SHORT(fid);
+ STRING(name, sizeof(f->name));
+ break;
+
+ case Topen:
+ SHORT(fid);
+ CHAR(mode);
+ break;
+
+ case Tcreate:
+ SHORT(fid);
+ STRING(name, sizeof(f->name));
+ LONG(perm);
+ CHAR(mode);
+ break;
+
+ case Tread:
+ SHORT(fid);
+ VLONG(offset);
+ SHORT(count);
+ break;
+
+ case Twrite:
+ SHORT(fid);
+ VLONG(offset);
+ SHORT(count);
+ p++; /* pad(1) */
+ f->data = (char*)p; p += f->count;
+ break;
+
+ case Tclunk:
+ SHORT(fid);
+ break;
+
+ case Tremove:
+ SHORT(fid);
+ break;
+
+ case Tstat:
+ SHORT(fid);
+ break;
+
+ case Twstat:
+ SHORT(fid);
+ STRING(stat, sizeof(f->stat));
+ break;
+
+ case Tclwalk:
+ SHORT(fid);
+ SHORT(newfid);
+ STRING(name, sizeof(f->name));
+ break;
+/*
+ */
+ case Rnop:
+ case Rosession:
+ break;
+
+ case Rsession:
+ STRING(chal, sizeof(f->chal));
+ STRING(authid, sizeof(f->authid));
+ STRING(authdom, sizeof(f->authdom));
+ break;
+
+ case Rerror:
+ STRING(ename, sizeof(f->ename));
+ break;
+
+ case Rflush:
+ break;
+
+ case Rattach:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ STRING(rauth, sizeof(f->rauth));
+ break;
+
+ case Roattach:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ break;
+
+ case Rauth:
+ SHORT(fid);
+ STRING(ticket, 8+8+7+7);
+ break;
+
+ case Rclone:
+ SHORT(fid);
+ break;
+
+ case Rwalk:
+ case Rclwalk:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ break;
+
+ case Ropen:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ break;
+
+ case Rcreate:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ break;
+
+ case Rread:
+ SHORT(fid);
+ SHORT(count);
+ p++; /* pad(1) */
+ f->data = (char*)p; p += f->count;
+ break;
+
+ case Rwrite:
+ SHORT(fid);
+ SHORT(count);
+ break;
+
+ case Rclunk:
+ SHORT(fid);
+ break;
+
+ case Rremove:
+ SHORT(fid);
+ break;
+
+ case Rstat:
+ SHORT(fid);
+ STRING(stat, sizeof(f->stat));
+ break;
+
+ case Rwstat:
+ SHORT(fid);
+ break;
+ }
+ if((uchar*)ap+n == p)
+ return n;
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/_fdinfo.c b/sys/src/ape/lib/ap/plan9/_fdinfo.c
new file mode 100755
index 000000000..85bbeca95
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/_fdinfo.c
@@ -0,0 +1,169 @@
+#define _BSDTIME_EXTENSION
+#include "lib.h"
+#include <sys/stat.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include <string.h>
+
+extern int errno;
+Fdinfo _fdinfo[OPEN_MAX];
+
+/*
+ called from _envsetup, either with the value of the environment
+ variable _fdinfo (from s to se-1), or with s==0 if there was no _fdinfo
+*/
+static void
+defaultfdinit(void)
+{
+ int i;
+ Fdinfo *fi;
+
+ for(i = 0; i <= 2; i++) {
+ fi = &_fdinfo[i];
+ fi->flags = FD_ISOPEN;
+ fi->oflags = (i == 0)? O_RDONLY : O_WRONLY;
+ if(_isatty(i))
+ fi->flags |= FD_ISTTY;
+ }
+}
+
+static int
+readprocfdinit(void)
+{
+ /* construct info from /proc/$pid/fd */
+ char buf[8192];
+ Fdinfo *fi;
+ int fd, pfd, pid, n, tot, m;
+ char *s, *nexts;
+
+ memset(buf, 0, sizeof buf);
+ pfd = _OPEN("#c/pid", 0);
+ if(pfd < 0)
+ return -1;
+ if(_PREAD(pfd, buf, 100, 0) < 0){
+ _CLOSE(pfd);
+ return -1;
+ }
+ _CLOSE(pfd);
+ pid = strtoul(buf, 0, 10);
+ strcpy(buf, "#p/");
+ _ultoa(buf+3, pid);
+ strcat(buf, "/fd");
+ pfd = _OPEN(buf, 0);
+ if(pfd < 0)
+ return -1;
+ memset(buf, 0, sizeof buf);
+ tot = 0;
+ for(;;){
+ n = _PREAD(pfd, buf+tot, sizeof buf-tot, tot);
+ if(n <= 0)
+ break;
+ tot += n;
+ }
+ _CLOSE(pfd);
+ if(n < 0)
+ return -1;
+ buf[sizeof buf-1] = '\0';
+ s = strchr(buf, '\n'); /* skip current directory */
+ if(s == 0)
+ return -1;
+ s++;
+ m = 0;
+ for(; s && *s; s=nexts){
+ nexts = strchr(s, '\n');
+ if(nexts)
+ *nexts++ = '\0';
+ errno = 0;
+ fd = strtoul(s, &s, 10);
+ if(errno != 0)
+ return -1;
+ if(fd >= OPEN_MAX)
+ continue;
+ if(fd == pfd)
+ continue;
+ fi = &_fdinfo[fd];
+ fi->flags = FD_ISOPEN;
+ while(*s == ' ' || *s == '\t')
+ s++;
+ if(*s == 'r'){
+ m |= 1;
+ s++;
+ }
+ if(*s == 'w'){
+ m |= 2;
+ }
+ if(m==1)
+ fi->oflags = O_RDONLY;
+ else if(m==2)
+ fi->oflags = O_WRONLY;
+ else
+ fi->oflags = O_RDWR;
+ if(strlen(s) >= 9 && strcmp(s+strlen(s)-9, "/dev/cons") == 0)
+ fi->flags |= FD_ISTTY;
+ }
+ return 0;
+}
+
+static void
+sfdinit(int usedproc, char *s, char *se)
+{
+ int i;
+ Fdinfo *fi;
+ unsigned long fd, fl, ofl;
+ char *e;
+ struct stat sbuf;
+
+ while(s < se){
+ fd = strtoul(s, &e, 10);
+ if(s == e)
+ break;
+ s = e;
+ fl = strtoul(s, &e, 10);
+ if(s == e)
+ break;
+ s = e;
+ ofl = strtoul(s, &e, 10);
+ if(s == e)
+ break;
+ s = e;
+ if(fd < OPEN_MAX){
+ fi = &_fdinfo[fd];
+ if(usedproc && !(fi->flags&FD_ISOPEN))
+ continue; /* should probably ignore all of $_fdinit */
+ fi->flags = fl;
+ fi->oflags = ofl;
+ if(_isatty(fd))
+ fi->flags |= FD_ISTTY;
+ }
+ }
+
+}
+
+void
+_fdinit(char *s, char *se)
+{
+ int i, usedproc;
+ Fdinfo *fi;
+ struct stat sbuf;
+
+ usedproc = 0;
+ if(readprocfdinit() == 0)
+ usedproc = 1;
+else
+_WRITE(2, "FAILED\n", 7);
+ if(s)
+ sfdinit(usedproc, s, se);
+ if(!s && !usedproc)
+ defaultfdinit();
+
+ for(i = 0; i < OPEN_MAX; i++) {
+ fi = &_fdinfo[i];
+ if(fi->flags&FD_ISOPEN){
+ if(fstat(i, &sbuf) >= 0) {
+ fi->uid = sbuf.st_uid;
+ fi->gid = sbuf.st_gid;
+ }
+ }
+ }
+}
+
diff --git a/sys/src/ape/lib/ap/plan9/_getpw.c b/sys/src/ape/lib/ap/plan9/_getpw.c
new file mode 100755
index 000000000..151d7f3c8
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/_getpw.c
@@ -0,0 +1,174 @@
+#include "lib.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "sys9.h"
+#include "dir.h"
+
+/*
+ * Search /adm/users for line with second field == *pname (if
+ * not NULL), else with first field == *pnum. Return non-zero
+ * if found, and fill in *pnum, *pname, and *plist to fields
+ * 1, 2, and 4
+ */
+
+enum {NAMEMAX = 20, MEMOMAX = 40 };
+
+static char *admusers = "/adm/users";
+
+/* we hold a fixed-length memo list of past lookups, and use a move-to-front
+ strategy to organize the list
+*/
+typedef struct Memo {
+ char name[NAMEMAX];
+ int num;
+ char *glist;
+} Memo;
+
+static Memo *memo[MEMOMAX];
+static int nmemo = 0;
+
+int
+_getpw(int *pnum, char **pname, char **plist)
+{
+ Dir *d;
+ int f, n, i, j, matchnum, m, matched;
+ char *eline, *f1, *f2, *f3, *f4;
+ Memo *mem;
+ static char *au = NULL;
+ vlong length;
+
+ if(!pname)
+ return 0;
+ if(au == NULL){
+ d = _dirstat(admusers);
+ if(d == nil)
+ return 0;
+ length = d->length;
+ free(d);
+ if((au = (char *)malloc(length+2)) == NULL)
+ return 0;
+ f = open(admusers, O_RDONLY);
+ if(f < 0)
+ return 0;
+ n = read(f, au, length);
+ if(n < 0)
+ return 0;
+ au[n] = 0;
+ }
+ matchnum = (*pname == NULL);
+ matched = 0;
+ /* try using memo */
+ for(i = 0; i<nmemo; i++) {
+ mem = memo[i];
+ if(matchnum)
+ matched = (mem->num == *pnum);
+ else
+ matched = (strcmp(mem->name, *pname) == 0);
+ if(matched) {
+ break;
+ }
+ }
+ if(!matched)
+ for(f1 = au, eline = au; !matched && *eline; f1 = eline+1){
+ eline = strchr(f1, '\n');
+ if(!eline)
+ eline = strchr(f1, 0);
+ if(*f1 == '#' || *f1 == '\n')
+ continue;
+ n = eline-f1;
+ f2 = memchr(f1, ':', n);
+ if(!f2)
+ continue;
+ f2++;
+ f3 = memchr(f2, ':', n-(f2-f1));
+ if(!f3)
+ continue;
+ f3++;
+ f4 = memchr(f3, ':', n-(f3-f1));
+ if(!f4)
+ continue;
+ f4++;
+ if(matchnum)
+ matched = (atoi(f1) == *pnum);
+ else{
+ int length;
+
+ length = f3-f2-1;
+ matched = length==strlen(*pname) && memcmp(*pname, f2, length)==0;
+ }
+ if(matched){
+ /* allocate and fill in a Memo structure */
+ mem = (Memo*)malloc(sizeof(struct Memo));
+ if(!mem)
+ return 0;
+ m = (f3-f2)-1;
+ if(m > NAMEMAX-1)
+ m = NAMEMAX-1;
+ memcpy(mem->name, f2, m);
+ mem->name[m] = 0;
+ mem->num = atoi(f1);
+ m = n-(f4-f1);
+ if(m > 0){
+ mem->glist = (char*)malloc(m+1);
+ if(mem->glist) {
+ memcpy(mem->glist, f4, m);
+ mem->glist[m] = 0;
+ }
+ } else
+ mem->glist = 0;
+ /* prepare for following move-to-front */
+ if(nmemo == MEMOMAX) {
+ free(memo[nmemo-1]);
+ i = nmemo-1;
+ } else {
+ i = nmemo++;
+ }
+ }
+ }
+ if(matched) {
+ if(matchnum)
+ *pname = mem->name;
+ else
+ *pnum = mem->num;
+ if(plist)
+ *plist = mem->glist;
+ if(i > 0) {
+ /* make room at front */
+ for(j = i; j > 0; j--)
+ memo[j] = memo[j-1];
+ }
+ memo[0] = mem;
+ return 1;
+ }
+ return 0;
+}
+
+char **
+_grpmems(char *list)
+{
+ char **v;
+ char *p;
+ static char *holdvec[200];
+ static char holdlist[1000];
+
+ p = list;
+ v = holdvec;
+ if(p) {
+ strncpy(holdlist, list, sizeof(holdlist));
+ while(v< &holdvec[sizeof(holdvec)]-1 && *p){
+ *v++ = p;
+ p = strchr(p, ',');
+ if(p){
+ p++;
+ *p = 0;
+ }else
+ break;
+ }
+ }
+ *v = 0;
+ return holdvec;
+}
diff --git a/sys/src/ape/lib/ap/plan9/_nap.c b/sys/src/ape/lib/ap/plan9/_nap.c
new file mode 100755
index 000000000..d21eff79c
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/_nap.c
@@ -0,0 +1,20 @@
+#include "lib.h"
+#include <unistd.h>
+#include <time.h>
+#include "sys9.h"
+
+/*
+ * This is an extension to POSIX
+ */
+unsigned int
+_nap(unsigned int millisecs)
+{
+ time_t t0, t1;
+
+ t0 = time(0);
+ if(_SLEEP(millisecs) < 0){
+ t1 = time(0);
+ return t1-t0;
+ }
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/access.c b/sys/src/ape/lib/ap/plan9/access.c
new file mode 100755
index 000000000..c51b37d6c
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/access.c
@@ -0,0 +1,62 @@
+#include "lib.h"
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include "dir.h"
+
+int
+access(const char *name, int mode)
+{
+ int fd, n;
+ Dir *db;
+ struct stat st;
+ static char omode[] = {
+ 0,
+ 3,
+ 1,
+ 2,
+ 0,
+ 2,
+ 2,
+ 2
+ };
+ char tname[1024];
+
+ if(mode == 0){
+ db = _dirstat(name);
+ if(db == nil){
+ _syserrno();
+ return -1;
+ }
+ free(db);
+ return 0;
+ }
+ fd = open(name, omode[mode&7]);
+ if(fd >= 0){
+ close(fd);
+ return 0;
+ }
+ else if(stat(name, &st)==0 && S_ISDIR(st.st_mode)){
+ if(mode & (R_OK|X_OK)){
+ fd = open(name, O_RDONLY);
+ if(fd < 0)
+ return -1;
+ close(fd);
+ }
+ if(mode & W_OK){
+ strncpy(tname, name, sizeof(tname)-9);
+ strcat(tname, "/_AcChAcK");
+ fd = creat(tname, 0666);
+ if(fd < 0)
+ return -1;
+ close(fd);
+ _REMOVE(tname);
+ }
+ return 0;
+ }
+ return -1;
+}
diff --git a/sys/src/ape/lib/ap/plan9/acid.c b/sys/src/ape/lib/ap/plan9/acid.c
new file mode 100755
index 000000000..5109a215e
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/acid.c
@@ -0,0 +1,31 @@
+/* include struct defs to get acid library
+ cpp -I/sys/include/ape -I/$objtype/include/ape -I./include acid.c > t.c
+ vc -a t.c > acidlib
+*/
+#define _POSIX_SOURCE 1
+#define _BSD_EXTENSION 1
+#define _LOCK_EXTENSION
+#include <stddef.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <lock.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <math.h>
+#include <float.h>
+#include <sys/utsname.h>
+/* #include "lib.h" buf.c below */
+/* #include "sys9.h" buf.c below */
+#include "_buf.c"
+#include "dir.h"
+#include "fcall.h"
diff --git a/sys/src/ape/lib/ap/plan9/acidlib b/sys/src/ape/lib/ap/plan9/acidlib
new file mode 100755
index 000000000..a4f14503f
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/acidlib
@@ -0,0 +1,706 @@
+sizeof_1_ = 8;
+aggr _1_
+{
+ 'D' 0 quot;
+ 'D' 4 rem;
+};
+
+defn
+_1_(addr) {
+ complex _1_ addr;
+ print(" quot ", addr.quot, "\n");
+ print(" rem ", addr.rem, "\n");
+};
+
+sizeof_2_ = 8;
+aggr _2_
+{
+ 'D' 0 quot;
+ 'D' 4 rem;
+};
+
+defn
+_2_(addr) {
+ complex _2_ addr;
+ print(" quot ", addr.quot, "\n");
+ print(" rem ", addr.rem, "\n");
+};
+
+sizeofsigaction = 12;
+aggr sigaction
+{
+ 'X' 0 sa_handler;
+ 'D' 4 sa_mask;
+ 'D' 8 sa_flags;
+};
+
+defn
+sigaction(addr) {
+ complex sigaction addr;
+ print(" sa_handler ", addr.sa_handler\X, "\n");
+ print(" sa_mask ", addr.sa_mask, "\n");
+ print(" sa_flags ", addr.sa_flags, "\n");
+};
+
+sizeof_3_ = 32;
+aggr _3_
+{
+ 'D' 0 fd;
+ 'C' 4 flags;
+ 'C' 5 state;
+ 'X' 8 buf;
+ 'X' 12 rp;
+ 'X' 16 wp;
+ 'X' 20 lp;
+ 'U' 24 bufl;
+ 'a' 28 unbuf;
+};
+
+defn
+_3_(addr) {
+ complex _3_ addr;
+ print(" fd ", addr.fd, "\n");
+ print(" flags ", addr.flags, "\n");
+ print(" state ", addr.state, "\n");
+ print(" buf ", addr.buf\X, "\n");
+ print(" rp ", addr.rp\X, "\n");
+ print(" wp ", addr.wp\X, "\n");
+ print(" lp ", addr.lp\X, "\n");
+ print(" bufl ", addr.bufl, "\n");
+ print(" unbuf ", addr.unbuf, "\n");
+};
+
+sizeof_4_ = 4;
+aggr _4_
+{
+ 'D' 0 val;
+};
+
+defn
+_4_(addr) {
+ complex _4_ addr;
+ print(" val ", addr.val, "\n");
+};
+
+sizeoftimeval = 8;
+aggr timeval
+{
+ 'D' 0 tv_sec;
+ 'D' 4 tv_usec;
+};
+
+defn
+timeval(addr) {
+ complex timeval addr;
+ print(" tv_sec ", addr.tv_sec, "\n");
+ print(" tv_usec ", addr.tv_usec, "\n");
+};
+
+sizeoftimezone = 8;
+aggr timezone
+{
+ 'D' 0 tz_minuteswest;
+ 'D' 4 tz_dsttime;
+};
+
+defn
+timezone(addr) {
+ complex timezone addr;
+ print(" tz_minuteswest ", addr.tz_minuteswest, "\n");
+ print(" tz_dsttime ", addr.tz_dsttime, "\n");
+};
+
+sizeoffd_set = 12;
+aggr fd_set
+{
+ 'a' 0 fds_bits;
+};
+
+defn
+fd_set(addr) {
+ complex fd_set addr;
+ mem(addr, "3X");
+};
+
+sizeofstat = 28;
+aggr stat
+{
+ 'u' 0 st_dev;
+ 'u' 2 st_ino;
+ 'u' 4 st_mode;
+ 'd' 6 st_nlink;
+ 'd' 8 st_uid;
+ 'd' 10 st_gid;
+ 'D' 12 st_size;
+ 'D' 16 st_atime;
+ 'D' 20 st_mtime;
+ 'D' 24 st_ctime;
+};
+
+defn
+stat(addr) {
+ complex stat addr;
+ print(" st_dev ", addr.st_dev, "\n");
+ print(" st_ino ", addr.st_ino, "\n");
+ print(" st_mode ", addr.st_mode, "\n");
+ print(" st_nlink ", addr.st_nlink, "\n");
+ print(" st_uid ", addr.st_uid, "\n");
+ print(" st_gid ", addr.st_gid, "\n");
+ print(" st_size ", addr.st_size, "\n");
+ print(" st_atime ", addr.st_atime, "\n");
+ print(" st_mtime ", addr.st_mtime, "\n");
+ print(" st_ctime ", addr.st_ctime, "\n");
+};
+
+sizeofflock = 16;
+aggr flock
+{
+ 'd' 0 l_type;
+ 'd' 2 l_whence;
+ 'D' 4 l_start;
+ 'D' 8 l_len;
+ 'D' 12 l_pid;
+};
+
+defn
+flock(addr) {
+ complex flock addr;
+ print(" l_type ", addr.l_type, "\n");
+ print(" l_whence ", addr.l_whence, "\n");
+ print(" l_start ", addr.l_start, "\n");
+ print(" l_len ", addr.l_len, "\n");
+ print(" l_pid ", addr.l_pid, "\n");
+};
+
+sizeofdirent = 28;
+aggr dirent
+{
+ 'a' 0 d_name;
+};
+
+defn
+dirent(addr) {
+ complex dirent addr;
+ print(" d_name ", addr.d_name, "\n");
+};
+
+sizeof_dirdesc = 16;
+aggr _dirdesc
+{
+ 'D' 0 dd_fd;
+ 'D' 4 dd_loc;
+ 'D' 8 dd_size;
+ 'X' 12 dd_buf;
+};
+
+defn
+_dirdesc(addr) {
+ complex _dirdesc addr;
+ print(" dd_fd ", addr.dd_fd, "\n");
+ print(" dd_loc ", addr.dd_loc, "\n");
+ print(" dd_size ", addr.dd_size, "\n");
+ print(" dd_buf ", addr.dd_buf\X, "\n");
+};
+
+sizeoftermios = 28;
+aggr termios
+{
+ 'U' 0 c_iflag;
+ 'U' 4 c_oflag;
+ 'U' 8 c_cflag;
+ 'U' 12 c_lflag;
+ 'a' 16 c_cc;
+};
+
+defn
+termios(addr) {
+ complex termios addr;
+ print(" c_iflag ", addr.c_iflag, "\n");
+ print(" c_oflag ", addr.c_oflag, "\n");
+ print(" c_cflag ", addr.c_cflag, "\n");
+ print(" c_lflag ", addr.c_lflag, "\n");
+ print(" c_cc ", addr.c_cc, "\n");
+};
+
+sizeofutsname = 20;
+aggr utsname
+{
+ 'X' 0 sysname;
+ 'X' 4 nodename;
+ 'X' 8 release;
+ 'X' 12 version;
+ 'X' 16 machine;
+};
+
+defn
+utsname(addr) {
+ complex utsname addr;
+ print(" sysname ", addr.sysname\X, "\n");
+ print(" nodename ", addr.nodename\X, "\n");
+ print(" release ", addr.release\X, "\n");
+ print(" version ", addr.version\X, "\n");
+ print(" machine ", addr.machine\X, "\n");
+};
+
+sizeofMuxbuf = 16400;
+aggr Muxbuf
+{
+ 'D' 0 n;
+ 'X' 4 putnext;
+ 'X' 8 getnext;
+ 'b' 12 fd;
+ 'b' 13 eof;
+ 'b' 14 roomwait;
+ 'b' 15 datawait;
+ 'a' 16 data;
+};
+
+defn
+Muxbuf(addr) {
+ complex Muxbuf addr;
+ print(" n ", addr.n, "\n");
+ print(" putnext ", addr.putnext\X, "\n");
+ print(" getnext ", addr.getnext\X, "\n");
+ print(" fd ", addr.fd, "\n");
+ print(" eof ", addr.eof, "\n");
+ print(" roomwait ", addr.roomwait, "\n");
+ print(" datawait ", addr.datawait, "\n");
+ print(" data ", addr.data, "\n");
+};
+
+sizeofFdinfo = 16;
+aggr Fdinfo
+{
+ 'U' 0 flags;
+ 'U' 4 oflags;
+ 'X' 8 name;
+ 'A' Muxbuf 12 buf;
+};
+
+defn
+Fdinfo(addr) {
+ complex Fdinfo addr;
+ print(" flags ", addr.flags, "\n");
+ print(" oflags ", addr.oflags, "\n");
+ print(" name ", addr.name\X, "\n");
+ print(" buf ", addr.buf\X, "\n");
+};
+
+sizeofWaitmsg = 112;
+aggr Waitmsg
+{
+ 'a' 0 pid;
+ 'a' 12 time;
+ 'a' 48 msg;
+};
+
+defn
+Waitmsg(addr) {
+ complex Waitmsg addr;
+ print(" pid ", addr.pid, "\n");
+ print(" time ", addr.time, "\n");
+ print(" msg ", addr.msg, "\n");
+};
+
+sizeof_5_ = 8;
+aggr _5_
+{
+ 'D' 0 hlength;
+ 'D' 4 length;
+};
+
+defn
+_5_(addr) {
+ complex _5_ addr;
+ print(" hlength ", addr.hlength, "\n");
+ print(" length ", addr.length, "\n");
+};
+
+sizeof_6_ = 8;
+aggr _6_
+{
+ 'a' 0 clength;
+ 'D' 0 vlength;
+ {
+ 'D' 0 hlength;
+ 'D' 4 length;
+ };
+};
+
+defn
+_6_(addr) {
+ complex _6_ addr;
+ print(" clength ", addr.clength, "\n");
+ print(" vlength ", addr.vlength, "\n");
+ print("_5_ {\n");
+ _5_(addr+0);
+ print("}\n");
+};
+
+sizeofQid = 8;
+aggr Qid
+{
+ 'U' 0 path;
+ 'U' 4 vers;
+};
+
+defn
+Qid(addr) {
+ complex Qid addr;
+ print(" path ", addr.path, "\n");
+ print(" vers ", addr.vers, "\n");
+};
+
+sizeofDir = 116;
+aggr Dir
+{
+ 'a' 0 name;
+ 'a' 28 uid;
+ 'a' 56 gid;
+ Qid 84 qid;
+ 'U' 92 mode;
+ 'D' 96 atime;
+ 'D' 100 mtime;
+ {
+ 'a' 104 clength;
+ 'D' 104 vlength;
+ {
+ 'D' 104 hlength;
+ 'D' 108 length;
+ };
+ };
+ 'd' 112 type;
+ 'd' 114 dev;
+};
+
+defn
+Dir(addr) {
+ complex Dir addr;
+ print(" name ", addr.name, "\n");
+ print(" uid ", addr.uid, "\n");
+ print(" gid ", addr.gid, "\n");
+ print("Qid qid {\n");
+ Qid(addr.qid);
+ print("}\n");
+ print(" mode ", addr.mode, "\n");
+ print(" atime ", addr.atime, "\n");
+ print(" mtime ", addr.mtime, "\n");
+ print("_6_ {\n");
+ _6_(addr+104);
+ print("}\n");
+ print(" type ", addr.type, "\n");
+ print(" dev ", addr.dev, "\n");
+};
+
+sizeof_7_ = 28;
+aggr _7_
+{
+ 'u' 0 oldtag;
+ Qid 4 qid;
+ 'a' 12 rauth;
+};
+
+defn
+_7_(addr) {
+ complex _7_ addr;
+ print(" oldtag ", addr.oldtag, "\n");
+ print("Qid qid {\n");
+ Qid(addr.qid);
+ print("}\n");
+ print(" rauth ", addr.rauth, "\n");
+};
+
+sizeof_8_ = 144;
+aggr _8_
+{
+ 'a' 0 uname;
+ 'a' 28 aname;
+ 'a' 56 ticket;
+ 'a' 128 auth;
+};
+
+defn
+_8_(addr) {
+ complex _8_ addr;
+ print(" uname ", addr.uname, "\n");
+ print(" aname ", addr.aname, "\n");
+ print(" ticket ", addr.ticket, "\n");
+ print(" auth ", addr.auth, "\n");
+};
+
+sizeof_9_ = 148;
+aggr _9_
+{
+ 'a' 0 ename;
+ 'a' 64 authid;
+ 'a' 92 authdom;
+ 'a' 140 chal;
+};
+
+defn
+_9_(addr) {
+ complex _9_ addr;
+ print(" ename ", addr.ename, "\n");
+ print(" authid ", addr.authid, "\n");
+ print(" authdom ", addr.authdom, "\n");
+ print(" chal ", addr.chal, "\n");
+};
+
+sizeof_10_ = 36;
+aggr _10_
+{
+ 'D' 0 perm;
+ 'd' 4 newfid;
+ 'a' 6 name;
+ 'C' 34 mode;
+};
+
+defn
+_10_(addr) {
+ complex _10_ addr;
+ print(" perm ", addr.perm, "\n");
+ print(" newfid ", addr.newfid, "\n");
+ print(" name ", addr.name, "\n");
+ print(" mode ", addr.mode, "\n");
+};
+
+sizeof_11_ = 12;
+aggr _11_
+{
+ 'D' 0 offset;
+ 'D' 4 count;
+ 'X' 8 data;
+};
+
+defn
+_11_(addr) {
+ complex _11_ addr;
+ print(" offset ", addr.offset, "\n");
+ print(" count ", addr.count, "\n");
+ print(" data ", addr.data\X, "\n");
+};
+
+sizeof_12_ = 116;
+aggr _12_
+{
+ 'a' 0 stat;
+};
+
+defn
+_12_(addr) {
+ complex _12_ addr;
+ print(" stat ", addr.stat, "\n");
+};
+
+sizeof_13_ = 148;
+aggr _13_
+{
+ {
+ 'u' 0 oldtag;
+ Qid 4 qid;
+ 'a' 12 rauth;
+ };
+ {
+ 'a' 0 uname;
+ 'a' 28 aname;
+ 'a' 56 ticket;
+ 'a' 128 auth;
+ };
+ {
+ 'a' 0 ename;
+ 'a' 64 authid;
+ 'a' 92 authdom;
+ 'a' 140 chal;
+ };
+ {
+ 'D' 0 perm;
+ 'd' 4 newfid;
+ 'a' 6 name;
+ 'C' 34 mode;
+ };
+ {
+ 'D' 0 offset;
+ 'D' 4 count;
+ 'X' 8 data;
+ };
+ {
+ 'a' 0 stat;
+ };
+};
+
+defn
+_13_(addr) {
+ complex _13_ addr;
+ print("_7_ {\n");
+ _7_(addr+0);
+ print("}\n");
+ print("_8_ {\n");
+ _8_(addr+0);
+ print("}\n");
+ print("_9_ {\n");
+ _9_(addr+0);
+ print("}\n");
+ print("_10_ {\n");
+ _10_(addr+0);
+ print("}\n");
+ print("_11_ {\n");
+ _11_(addr+0);
+ print("}\n");
+ print("_12_ {\n");
+ _12_(addr+0);
+ print("}\n");
+};
+
+sizeofFcall = 156;
+aggr Fcall
+{
+ 'C' 0 type;
+ 'd' 2 fid;
+ 'u' 4 tag;
+ {
+ {
+ 'u' 8 oldtag;
+ Qid 12 qid;
+ 'a' 20 rauth;
+ };
+ {
+ 'a' 8 uname;
+ 'a' 36 aname;
+ 'a' 64 ticket;
+ 'a' 136 auth;
+ };
+ {
+ 'a' 8 ename;
+ 'a' 72 authid;
+ 'a' 100 authdom;
+ 'a' 148 chal;
+ };
+ {
+ 'D' 8 perm;
+ 'd' 12 newfid;
+ 'a' 14 name;
+ 'C' 42 mode;
+ };
+ {
+ 'D' 8 offset;
+ 'D' 12 count;
+ 'X' 16 data;
+ };
+ {
+ 'a' 8 stat;
+ };
+ };
+};
+
+defn
+Fcall(addr) {
+ complex Fcall addr;
+ print(" type ", addr.type, "\n");
+ print(" fid ", addr.fid, "\n");
+ print(" tag ", addr.tag, "\n");
+ print("_13_ {\n");
+ _13_(addr+8);
+ print("}\n");
+};
+
+sizeofMuxbuf = 16400;
+aggr Muxbuf
+{
+ 'D' 0 n;
+ 'X' 4 putnext;
+ 'X' 8 getnext;
+ 'b' 12 fd;
+ 'b' 13 eof;
+ 'b' 14 roomwait;
+ 'b' 15 datawait;
+ 'a' 16 data;
+};
+
+defn
+Muxbuf(addr) {
+ complex Muxbuf addr;
+ print(" n ", addr.n, "\n");
+ print(" putnext ", addr.putnext\X, "\n");
+ print(" getnext ", addr.getnext\X, "\n");
+ print(" fd ", addr.fd, "\n");
+ print(" eof ", addr.eof, "\n");
+ print(" roomwait ", addr.roomwait, "\n");
+ print(" datawait ", addr.datawait, "\n");
+ print(" data ", addr.data, "\n");
+};
+
+sizeofFdinfo = 16;
+aggr Fdinfo
+{
+ 'U' 0 flags;
+ 'U' 4 oflags;
+ 'X' 8 name;
+ 'A' Muxbuf 12 buf;
+};
+
+defn
+Fdinfo(addr) {
+ complex Fdinfo addr;
+ print(" flags ", addr.flags, "\n");
+ print(" oflags ", addr.oflags, "\n");
+ print(" name ", addr.name\X, "\n");
+ print(" buf ", addr.buf\X, "\n");
+};
+
+sizeofWaitmsg = 112;
+aggr Waitmsg
+{
+ 'a' 0 pid;
+ 'a' 12 time;
+ 'a' 48 msg;
+};
+
+defn
+Waitmsg(addr) {
+ complex Waitmsg addr;
+ print(" pid ", addr.pid, "\n");
+ print(" time ", addr.time, "\n");
+ print(" msg ", addr.msg, "\n");
+};
+
+sizeofMuxseg = 65640;
+aggr Muxseg
+{
+ _4_ 0 lock;
+ 'D' 4 curfds;
+ 'D' 8 selwait;
+ 'D' 12 waittime;
+ fd_set 16 rwant;
+ fd_set 28 ewant;
+ 'a' 40 bufs;
+};
+
+defn
+Muxseg(addr) {
+ complex Muxseg addr;
+ print("_4_ lock {\n");
+ _4_(addr.lock);
+ print("}\n");
+ print(" curfds ", addr.curfds, "\n");
+ print(" selwait ", addr.selwait, "\n");
+ print(" waittime ", addr.waittime, "\n");
+ print("fd_set rwant {\n");
+ fd_set(addr.rwant);
+ print("}\n");
+ print("fd_set ewant {\n");
+ fd_set(addr.ewant);
+ print("}\n");
+ print(" bufs ", addr.bufs, "\n");
+};
+
+complex Muxseg mux;
+complex Fdinfo _startbuf:f;
+complex Muxbuf _startbuf:b;
+complex Muxbuf _copyproc:b;
+complex Muxbuf _readbuf:b;
+complex fd_set select:rfds;
+complex fd_set select:wfds;
+complex fd_set select:efds;
+complex timeval select:timeout;
+complex Fdinfo select:f;
+complex Muxbuf select:b;
diff --git a/sys/src/ape/lib/ap/plan9/alarm.c b/sys/src/ape/lib/ap/plan9/alarm.c
new file mode 100755
index 000000000..f8a24aa65
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/alarm.c
@@ -0,0 +1,9 @@
+#include "lib.h"
+#include <unistd.h>
+#include "sys9.h"
+
+unsigned int
+alarm(unsigned seconds)
+{
+ return _ALARM(seconds*1000);
+}
diff --git a/sys/src/ape/lib/ap/plan9/brk.c b/sys/src/ape/lib/ap/plan9/brk.c
new file mode 100755
index 000000000..0b3240377
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/brk.c
@@ -0,0 +1,36 @@
+#include "lib.h"
+#include <errno.h>
+#include "sys9.h"
+
+char end[];
+static char *bloc = { end };
+extern int _BRK_(void*);
+
+char *
+brk(char *p)
+{
+ unsigned long n;
+
+ n = (unsigned long)p;
+ n += 3;
+ n &= ~3;
+ if(_BRK_((void*)n) < 0){
+ errno = ENOMEM;
+ return (char *)-1;
+ }
+ bloc = (char *)n;
+ return 0;
+}
+
+void *
+sbrk(unsigned long n)
+{
+ n += 3;
+ n &= ~3;
+ if(_BRK_((void *)(bloc+n)) < 0){
+ errno = ENOMEM;
+ return (void *)-1;
+ }
+ bloc += n;
+ return (void *)(bloc-n);
+}
diff --git a/sys/src/ape/lib/ap/plan9/buf.prom b/sys/src/ape/lib/ap/plan9/buf.prom
new file mode 100755
index 000000000..e8751dadd
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/buf.prom
@@ -0,0 +1,360 @@
+#define NBUFS 2
+#define READMAX 2
+#define BUFSIZ 2*READMAX
+#define EOF 255
+#define TIMEOUT 254
+#define FILEMAXLEN 20
+
+byte n[NBUFS];
+byte ntotal[NBUFS];
+byte putnext[NBUFS];
+byte getnext[NBUFS];
+bool eof[NBUFS];
+bool roomwait[NBUFS];
+bool datawait[NBUFS];
+byte rwant;
+
+/* use one big data array to simulate 2-d array */
+#define bufstart(slot) (slot*BUFSIZ)
+#define bufend(slot) ((slot+1)*BUFSIZ)
+/* bit data[BUFSIZ*NBUFS]; */
+
+bool selwait;
+/* bool hastimeout; */
+
+#define get 0
+#define release 1
+
+chan lock = [0] of { bit };
+chan lockkill = [0] of { bit };
+chan sel = [0] of { byte };
+chan selcall = [0] of { byte };
+chan selans = [0] of { byte, byte };
+chan selkill = [0] of { bit };
+chan readcall = [0] of { byte, byte };
+chan readans = [0] of { byte };
+chan readkill = [0] of { bit };
+chan croom[NBUFS] = [0] of { bit };
+chan cdata[NBUFS] = [0] of { bit };
+
+proctype Lockrendez()
+{
+ do
+ :: lock!get -> lock?release
+ :: lockkill?release -> break
+ od
+}
+
+proctype Copy(byte fd)
+{
+ byte num;
+ bit b;
+
+ do :: 1 ->
+ /* make sure there's room */
+ lock?get;
+ if
+ :: (BUFSIZ-putnext[fd]) < READMAX ->
+ if
+ :: getnext[fd] == putnext[fd] ->
+ getnext[fd] = 0;
+ putnext[fd] = 0;
+ lock!release
+ :: getnext[fd] != putnext[fd] ->
+ roomwait[fd] = 1;
+ lock!release;
+ croom[fd]?b
+ fi
+ :: (BUFSIZ-putnext[fd]) >= READMAX ->
+ lock!release
+ fi;
+ /* simulate read into data buf at putnext */
+ if
+ :: ntotal[fd] > FILEMAXLEN ->
+ num = EOF
+ :: ntotal[fd] <= FILEMAXLEN ->
+ if
+ :: num = 1
+ :: num = READMAX
+ :: num = EOF
+ fi
+ fi;
+ /* here is where data transfer would happen */
+ lock?get;
+ if
+ :: num == EOF ->
+ eof[fd] = 1;
+/* printf("Copy %d got eof\n", fd);/**/
+ if
+ :: datawait[fd] ->
+ datawait[fd] = 0;
+ lock!release;
+ cdata[fd]!1
+ :: !datawait[fd] && (rwant & (1<<fd)) && selwait ->
+ selwait = 0;
+ lock!release;
+ sel!fd
+ :: !datawait[fd] && !((rwant & (1<<fd)) && selwait) ->
+ lock!release
+ fi;
+ break
+ :: num != EOF ->
+/* printf("Copy %d putting %d in; old putnext=%d, old n=%d\n", fd, num, putnext[fd], n[fd]); /* */
+ putnext[fd] = putnext[fd] + num;
+ n[fd] = n[fd] + num;
+ ntotal[fd] = ntotal[fd] + num;
+ assert(n[fd] > 0);
+ if
+ :: datawait[fd] ->
+ datawait[fd] = 0;
+ lock!release;
+ cdata[fd]!1
+ :: !datawait[fd] && (rwant & (1<<fd)) && selwait ->
+ selwait = 0;
+ lock!release;
+ sel!fd
+ :: !datawait[fd] && !((rwant & (1<<fd)) && selwait) ->
+ lock!release
+ fi
+ fi;
+ od
+}
+
+proctype Read()
+{
+ byte ngot;
+ byte fd;
+ byte nwant;
+ bit b;
+
+ do
+ :: readcall?fd,nwant ->
+ if
+ :: eof[fd] && n[fd] == 0 ->
+ readans!EOF
+ :: !(eof[fd] && n[fd] == 0) ->
+ lock?get;
+ ngot = putnext[fd] - getnext[fd];
+/* printf("Reading %d, want %d: ngot = %d - %d, n = %d\n", fd, nwant, putnext[fd], getnext[fd], n[fd]); /* */
+ if
+ :: ngot == 0 ->
+ if
+ :: eof[fd] ->
+ skip
+ :: !eof[fd] ->
+ /* sleep until there's data */
+ datawait[fd] = 1;
+/* printf("Read sleeping\n"); /* */
+ lock!release;
+ cdata[fd]?b;
+ lock?get;
+ ngot = putnext[fd] - getnext[fd];
+/* printf("Read awoke, ngot = %d\n", ngot); /**/
+ fi
+ :: ngot != 0 -> skip
+ fi;
+ if
+ :: ngot > nwant -> ngot = nwant
+ :: ngot <= nwant -> skip
+ fi;
+ /* here would take ngot elements from data, from getnext[fd] ... */
+ getnext[fd] = getnext[fd] + ngot;
+ assert(n[fd] >= ngot);
+ n[fd] = n[fd] - ngot;
+ if
+ :: ngot == 0 ->
+ assert(eof[fd]);
+ ngot = EOF
+ :: ngot != 0 -> skip
+ fi;
+ if
+ :: getnext[fd] == putnext[fd] && roomwait[fd] ->
+ getnext[fd] = 0;
+ putnext[fd] = 0;
+ roomwait[fd] = 0;
+ lock!release;
+ croom[fd]!0
+ :: getnext[fd] != putnext[fd] || !roomwait[fd] ->
+ lock!release
+ fi;
+ readans!ngot
+ fi
+ :: readkill?b -> break
+ od
+}
+
+proctype Select()
+{
+ byte num;
+ byte i;
+ byte fd;
+ byte r;
+ bit b;
+
+ do
+ :: selcall?r ->
+/* printf("Select called, r=%d\n", r); /**/
+ i = 0;
+ do
+ :: i < NBUFS ->
+ if
+ :: r & (1<<i) ->
+ if
+ :: eof[i] && n[i] == 0 ->
+/* printf("Select got eof on %d\n", i);/**/
+ num = EOF;
+ r = i;
+ goto donesel
+ :: !eof[i] || n[i] != 0 -> skip
+ fi
+ :: !(r & (1<<i)) -> skip
+ fi;
+ i = i+1
+ :: i >= NBUFS -> break
+ od;
+ num = 0;
+ lock?get;
+ rwant = 0;
+ i = 0;
+ do
+ :: i < NBUFS ->
+ if
+ :: r & (1<<i) ->
+ if
+ :: n[i] > 0 || eof[i] ->
+/* printf("Select found %d has n==%d\n", i, n[i]); /**/
+ num = num+1
+ :: n[i] == 0 && !eof[i] ->
+/* printf("Select asks to wait for %d\n", i); /**/
+ r = r &(~(1<<i));
+ rwant = rwant | (1<<i)
+ fi
+ :: !(r & (1<<i)) -> skip
+ fi;
+ i = i+1
+ :: i >= NBUFS -> break
+ od;
+ if
+ :: num > 0 || rwant == 0 ->
+ rwant = 0;
+ lock!release;
+ :: num == 0 && rwant != 0 ->
+ selwait = 1;
+ lock!release;
+/* printf("Select sleeps\n"); /**/
+ sel?fd;
+/* printf("Select wakes up, fd=%d\n", fd); /**/
+ if
+ :: fd != TIMEOUT ->
+ if
+ :: (rwant & (1<<fd)) && (n[fd] > 0) ->
+ r = r | (1<<fd);
+ num = 1
+ :: !(rwant & (1<<fd)) || (n[fd] == 0) ->
+ num = 0
+ fi
+ :: fd == TIMEOUT -> skip
+ fi;
+ rwant = 0
+ fi;
+ donesel:
+ selans!num,r
+ :: selkill?b -> break
+ od
+}
+
+/* This routine is written knowing NBUFS == 2 in several places */
+proctype User()
+{
+ byte ndone;
+ byte i;
+ byte rw;
+ byte num;
+ byte nwant;
+ byte fd;
+ bool goteof[NBUFS];
+
+ ndone = 0;
+ do
+ :: ndone == NBUFS -> break
+ :: ndone < NBUFS ->
+ if
+ :: 1->
+ /* maybe use Read */
+/* printf("User trying to read. Current goteofs: %d %d\n", goteof[0], goteof[1]); /**/
+ /* randomly pick fd 0 or 1 from non-eof ones */
+ if
+ :: !goteof[0] -> fd = 0
+ :: !goteof[1] -> fd = 1
+ fi;
+ if
+ :: nwant = 1
+ :: nwant = READMAX
+ fi;
+ readcall!fd,nwant;
+ readans?num;
+ if
+ :: num == EOF ->
+ goteof[fd] = 1;
+ ndone = ndone + 1
+ :: num != EOF -> assert(num != 0)
+ fi
+ :: 1->
+/* printf("User trying to select. Current goteofs: %d %d\n", goteof[0], goteof[1]); /**/
+ /* maybe use Select, then Read */
+ /* randomly set the "i want" bit for non-eof fds */
+ if
+ :: !goteof[0] && !goteof[1] -> rw = (1<<0) | (1<<1)
+ :: !goteof[0] -> rw = (1<<0)
+ :: !goteof[1] -> rw = (1<<1)
+ fi;
+ selcall!rw;
+ selans?i,rw;
+ if
+ :: i == EOF ->
+ goteof[rw] = 1;
+ ndone = ndone + 1
+ :: i != EOF ->
+ /* this next statement knows NBUFS == 2 ! */
+ if
+ :: rw & (1<<0) -> fd = 0
+ :: rw & (1<<1) -> fd = 1
+ :: rw == 0 -> fd = EOF
+ fi;
+ if
+ :: nwant = 1
+ :: nwant = READMAX
+ fi;
+ if
+ :: fd != EOF ->
+ readcall!fd,nwant;
+ readans?num;
+ assert(num != 0)
+ :: fd == EOF -> skip
+ fi
+ fi
+ fi
+ od;
+ lockkill!release;
+ selkill!release;
+ readkill!release
+}
+
+init
+{
+ byte i;
+
+ atomic {
+ run Lockrendez();
+ i = 0;
+ do
+ :: i < NBUFS ->
+ run Copy(i);
+ i = i+1
+ :: i >= NBUFS -> break
+ od;
+ run Select();
+ run Read();
+ run User()
+ }
+}
diff --git a/sys/src/ape/lib/ap/plan9/cfgetospeed.c b/sys/src/ape/lib/ap/plan9/cfgetospeed.c
new file mode 100755
index 000000000..cc89f4a86
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/cfgetospeed.c
@@ -0,0 +1,26 @@
+#include <termios.h>
+
+speed_t
+cfgetospeed(const struct termios *p)
+{
+ return B0;
+}
+
+int
+cfsetospeed(struct termios *p, speed_t s)
+{
+ return 0;
+}
+
+speed_t
+cfgetispeed(const struct termios *p)
+{
+ return B0;
+}
+
+int
+cfsetispeed(struct termios *p, speed_t s)
+{
+ return 0;
+}
+
diff --git a/sys/src/ape/lib/ap/plan9/chdir.c b/sys/src/ape/lib/ap/plan9/chdir.c
new file mode 100755
index 000000000..245f116d0
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/chdir.c
@@ -0,0 +1,14 @@
+#include "lib.h"
+#include <unistd.h>
+#include "sys9.h"
+
+int
+chdir(const char *f)
+{
+ int n;
+
+ n = _CHDIR(f);
+ if(n < 0)
+ _syserrno();
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/chmod.c b/sys/src/ape/lib/ap/plan9/chmod.c
new file mode 100755
index 000000000..8b6daad22
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/chmod.c
@@ -0,0 +1,33 @@
+#include "lib.h"
+#include <errno.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include "dir.h"
+
+int
+chmod(const char *path, mode_t mode)
+{
+ Dir d;
+
+ _nulldir(&d);
+ d.mode = mode & 0777;
+ if(_dirwstat(path, &d) < 0){
+ _syserrno();
+ return -1;
+ }
+ return 0;
+}
+
+int
+fchmod(int fd, mode_t mode)
+{
+ Dir d;
+
+ _nulldir(&d);
+ d.mode = mode & 0777;
+ if(_dirfwstat(fd, &d) < 0){
+ _syserrno();
+ return -1;
+ }
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/chown.c b/sys/src/ape/lib/ap/plan9/chown.c
new file mode 100755
index 000000000..22b651d14
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/chown.c
@@ -0,0 +1,39 @@
+#include "lib.h"
+#include "sys9.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "dir.h"
+
+int
+chown(const char *path, uid_t owner, gid_t group)
+{
+ int num;
+ Dir d;
+
+ _nulldir(&d);
+
+ /* find owner, group */
+ d.uid = nil;
+ num = owner;
+ if(!_getpw(&num, &d.uid, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ d.gid = nil;
+ num = group;
+ if(!_getpw(&num, &d.gid, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if(_dirwstat(path, &d) < 0){
+ _syserrno();
+ return -1;
+ }
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/close.c b/sys/src/ape/lib/ap/plan9/close.c
new file mode 100755
index 000000000..258788a39
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/close.c
@@ -0,0 +1,34 @@
+#include "lib.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include "sys9.h"
+
+int
+close(int d)
+{
+ int n;
+ Fdinfo *f;
+
+ n = -1;
+ f = &_fdinfo[d];
+ if(d<0 || d>=OPEN_MAX || !(f->flags&FD_ISOPEN))
+ errno = EBADF;
+ else{
+ if(f->flags&(FD_BUFFERED|FD_BUFFEREDX)) {
+ if(f->flags&FD_BUFFERED)
+ _closebuf(d);
+ f->flags &= ~FD_BUFFERED;
+ }
+ n = _CLOSE(d);
+ if(n < 0)
+ _syserrno();
+ _fdinfo[d].flags = 0;
+ _fdinfo[d].oflags = 0;
+ if(_fdinfo[d].name){
+ free(_fdinfo[d].name);
+ _fdinfo[d].name = 0;
+ }
+ }
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/convD2M.c b/sys/src/ape/lib/ap/plan9/convD2M.c
new file mode 100755
index 000000000..fc1ade5c5
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/convD2M.c
@@ -0,0 +1,93 @@
+#include "lib.h"
+#include <string.h>
+#include "sys9.h"
+#include "dir.h"
+
+uint
+_convD2M(Dir *d, uchar *buf, uint nbuf)
+{
+ uchar *p, *ebuf;
+ char *sv[4];
+ int i, ns, nsv[4], ss;
+
+ if(nbuf < BIT16SZ)
+ return 0;
+
+ p = buf;
+ ebuf = buf + nbuf;
+
+ sv[0] = d->name;
+ sv[1] = d->uid;
+ sv[2] = d->gid;
+ sv[3] = d->muid;
+
+ ns = 0;
+ for(i = 0; i < 4; i++){
+ nsv[i] = strlen(sv[i]);
+ ns += nsv[i];
+ }
+
+ ss = STATFIXLEN + ns;
+
+ /* set size befor erroring, so user can know how much is needed */
+ /* note that length excludes count field itself */
+ PBIT16(p, ss-BIT16SZ);
+ p += BIT16SZ;
+
+ if(ss > nbuf)
+ return BIT16SZ;
+
+ PBIT16(p, d->type);
+ p += BIT16SZ;
+ PBIT32(p, d->dev);
+ p += BIT32SZ;
+ PBIT8(p, d->qid.type);
+ p += BIT8SZ;
+ PBIT32(p, d->qid.vers);
+ p += BIT32SZ;
+ PBIT64(p, d->qid.path);
+ p += BIT64SZ;
+ PBIT32(p, d->mode);
+ p += BIT32SZ;
+ PBIT32(p, d->atime);
+ p += BIT32SZ;
+ PBIT32(p, d->mtime);
+ p += BIT32SZ;
+ PBIT64(p, d->length);
+ p += BIT64SZ;
+
+ for(i = 0; i < 4; i++){
+ ns = nsv[i];
+ if(p + ns + BIT16SZ > ebuf)
+ return 0;
+ PBIT16(p, ns);
+ p += BIT16SZ;
+ memmove(p, sv[i], ns);
+ p += ns;
+ }
+
+ if(ss != p - buf)
+ return 0;
+
+ return p - buf;
+}
+
+uint
+_sizeD2M(Dir *d)
+{
+ char *sv[4];
+ int i, ns;
+
+ sv[0] = d->name;
+ sv[1] = d->uid;
+ sv[2] = d->gid;
+ sv[3] = d->muid;
+
+ ns = 0;
+ for(i = 0; i < 4; i++)
+ if(sv[i])
+ ns += strlen(sv[i]);
+
+ return STATFIXLEN + ns;
+}
+
diff --git a/sys/src/ape/lib/ap/plan9/convM2D.c b/sys/src/ape/lib/ap/plan9/convM2D.c
new file mode 100755
index 000000000..222e88927
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/convM2D.c
@@ -0,0 +1,74 @@
+#include "lib.h"
+#include <string.h>
+#include "sys9.h"
+#include "dir.h"
+#define nil ((void*)0)
+
+static char nullstring[] = "";
+
+uint
+_convM2D(uchar *buf, uint nbuf, Dir *d, char *strs)
+{
+ uchar *p, *ebuf;
+ char *sv[4];
+ int i, ns, nsv[4];
+
+ p = buf;
+ ebuf = buf + nbuf;
+
+ p += BIT16SZ; /* ignore size */
+ d->type = GBIT16(p);
+ p += BIT16SZ;
+ d->dev = GBIT32(p);
+ p += BIT32SZ;
+ d->qid.type = GBIT8(p);
+ p += BIT8SZ;
+ d->qid.vers = GBIT32(p);
+ p += BIT32SZ;
+ d->qid.path = GBIT64(p);
+ p += BIT64SZ;
+ d->mode = GBIT32(p);
+ p += BIT32SZ;
+ d->atime = GBIT32(p);
+ p += BIT32SZ;
+ d->mtime = GBIT32(p);
+ p += BIT32SZ;
+ d->length = GBIT64(p);
+ p += BIT64SZ;
+
+ d->name = nil;
+ d->uid = nil;
+ d->gid = nil;
+ d->muid = nil;
+
+ for(i = 0; i < 4; i++){
+ if(p + BIT16SZ > ebuf)
+ return 0;
+ ns = GBIT16(p);
+ p += BIT16SZ;
+ if(p + ns > ebuf)
+ return 0;
+ if(strs){
+ nsv[i] = ns;
+ sv[i] = strs;
+ memmove(strs, p, ns);
+ strs += ns;
+ *strs++ = '\0';
+ }
+ p += ns;
+ }
+
+ if(strs){
+ d->name = sv[0];
+ d->uid = sv[1];
+ d->gid = sv[2];
+ d->muid = sv[3];
+ }else{
+ d->name = nullstring;
+ d->uid = nullstring;
+ d->gid = nullstring;
+ d->muid = nullstring;
+ }
+
+ return p - buf;
+}
diff --git a/sys/src/ape/lib/ap/plan9/creat.c b/sys/src/ape/lib/ap/plan9/creat.c
new file mode 100755
index 000000000..a0f917a62
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/creat.c
@@ -0,0 +1,13 @@
+#include "lib.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int
+creat(const char *name, mode_t mode)
+{
+ int n;
+
+ n = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode);
+ /* no need to _syserrno; open did it already */
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/ctermid.c b/sys/src/ape/lib/ap/plan9/ctermid.c
new file mode 100755
index 000000000..a8689a71f
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/ctermid.c
@@ -0,0 +1,20 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+char *
+ctermid(char *s)
+{
+ static char buf[L_ctermid];
+
+ if(s == 0)
+ s = buf;
+ strncpy(s, "/dev/cons", sizeof buf);
+ return(s);
+}
+
+char *
+ctermid_r(char *s)
+{
+ return s ? ctermid(s) : NULL;
+}
diff --git a/sys/src/ape/lib/ap/plan9/ctime.c b/sys/src/ape/lib/ap/plan9/ctime.c
new file mode 100755
index 000000000..34a0b12a5
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/ctime.c
@@ -0,0 +1,321 @@
+/*
+ * This routine converts time as follows.
+ * The epoch is 0000 Jan 1 1970 GMT.
+ * The argument time is in seconds since then.
+ * The localtime(t) entry returns a pointer to an array
+ * containing
+ *
+ * seconds (0-59)
+ * minutes (0-59)
+ * hours (0-23)
+ * day of month (1-31)
+ * month (0-11)
+ * year-1970
+ * weekday (0-6, Sun is 0)
+ * day of the year
+ * daylight savings flag
+ *
+ * The routine gets the daylight savings time from the environment.
+ *
+ * asctime(tvec))
+ * where tvec is produced by localtime
+ * returns a ptr to a character string
+ * that has the ascii time in the form
+ *
+ * \\
+ * Thu Jan 01 00:00:00 1970n0
+ * 01234567890123456789012345
+ * 0 1 2
+ *
+ * ctime(t) just calls localtime, then asctime.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+
+static char dmsize[12] =
+{
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * The following table is used for 1974 and 1975 and
+ * gives the day number of the first day after the Sunday of the
+ * change.
+ */
+
+static int dysize(int);
+static void ct_numb(char*, int);
+static void readtimezone(void);
+static int rd_name(char**, char*);
+static int rd_long(char**, long*);
+
+#define TZSIZE 150
+
+static
+struct
+{
+ char stname[4];
+ char dlname[4];
+ long stdiff;
+ long dldiff;
+ long dlpairs[TZSIZE];
+} timezone;
+
+char*
+ctime(const time_t *t)
+{
+ return asctime(localtime(t));
+}
+
+struct tm*
+gmtime_r(const time_t *timp, struct tm *result)
+{
+ int d0, d1;
+ long hms, day;
+ time_t tim;
+
+ tim = *timp;
+ /*
+ * break initial number into days
+ */
+ hms = tim % 86400L;
+ day = tim / 86400L;
+ if(hms < 0) {
+ hms += 86400L;
+ day -= 1;
+ }
+
+ /*
+ * generate hours:minutes:seconds
+ */
+ result->tm_sec = hms % 60;
+ d1 = hms / 60;
+ result->tm_min = d1 % 60;
+ d1 /= 60;
+ result->tm_hour = d1;
+
+ /*
+ * day is the day number.
+ * generate day of the week.
+ * The addend is 4 mod 7 (1/1/1970 was Thursday)
+ */
+
+ result->tm_wday = (day + 7340036L) % 7;
+
+ /*
+ * year number
+ */
+ if(day >= 0)
+ for(d1 = 70; day >= dysize(d1); d1++)
+ day -= dysize(d1);
+ else
+ for (d1 = 70; day < 0; d1--)
+ day += dysize(d1-1);
+ result->tm_year = d1;
+ result->tm_yday = d0 = day;
+
+ /*
+ * generate month
+ */
+
+ if(dysize(d1) == 366)
+ dmsize[1] = 29;
+ for(d1 = 0; d0 >= dmsize[d1]; d1++)
+ d0 -= dmsize[d1];
+ dmsize[1] = 28;
+ result->tm_mday = d0 + 1;
+ result->tm_mon = d1;
+ result->tm_isdst = 0;
+ return result;
+}
+
+struct tm*
+gmtime(const time_t *timp)
+{
+ static struct tm xtime;
+
+ return gmtime_r(timp, &xtime);
+}
+
+struct tm*
+localtime_r(const time_t *timp, struct tm *result)
+{
+ struct tm *ct;
+ time_t t, tim;
+ long *p;
+ int i, dlflag;
+
+ tim = *timp;
+ if(timezone.stname[0] == 0)
+ readtimezone();
+ t = tim + timezone.stdiff;
+ dlflag = 0;
+ for(p = timezone.dlpairs; *p; p += 2)
+ if(t >= p[0])
+ if(t < p[1]) {
+ t = tim + timezone.dldiff;
+ dlflag++;
+ break;
+ }
+ ct = gmtime_r(&t, result);
+ ct->tm_isdst = dlflag;
+ return ct;
+}
+
+struct tm*
+localtime(const time_t *timp)
+{
+ static struct tm xtime;
+
+ return localtime_r(timp, &xtime);
+}
+
+char*
+asctime_r(const struct tm *t, char *buf)
+{
+ char *ncp;
+
+ strcpy(buf, "Thu Jan 01 00:00:00 1970\n");
+ ncp = &"SunMonTueWedThuFriSat"[t->tm_wday*3];
+ buf[0] = *ncp++;
+ buf[1] = *ncp++;
+ buf[2] = *ncp;
+ ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[t->tm_mon*3];
+ buf[4] = *ncp++;
+ buf[5] = *ncp++;
+ buf[6] = *ncp;
+ ct_numb(buf+8, t->tm_mday);
+ ct_numb(buf+11, t->tm_hour+100);
+ ct_numb(buf+14, t->tm_min+100);
+ ct_numb(buf+17, t->tm_sec+100);
+ if(t->tm_year >= 100) {
+ buf[20] = '2';
+ buf[21] = '0';
+ }
+ ct_numb(buf+22, t->tm_year+100);
+ return buf;
+}
+
+char*
+asctime(const struct tm *t)
+{
+ static char cbuf[30];
+
+ return asctime_r(t, cbuf);
+}
+
+static
+dysize(int y)
+{
+ if((y%4) == 0)
+ return 366;
+ return 365;
+}
+
+static
+void
+ct_numb(char *cp, int n)
+{
+ cp[0] = ' ';
+ if(n >= 10)
+ cp[0] = (n/10)%10 + '0';
+ cp[1] = n%10 + '0';
+}
+
+static
+void
+readtimezone(void)
+{
+ char buf[TZSIZE*11+30], *p;
+ int i;
+
+ memset(buf, 0, sizeof(buf));
+ i = open("/env/timezone", 0);
+ if(i < 0)
+ goto error;
+ if(read(i, buf, sizeof(buf)) >= sizeof(buf))
+ goto error;
+ close(i);
+ p = buf;
+ if(rd_name(&p, timezone.stname))
+ goto error;
+ if(rd_long(&p, &timezone.stdiff))
+ goto error;
+ if(rd_name(&p, timezone.dlname))
+ goto error;
+ if(rd_long(&p, &timezone.dldiff))
+ goto error;
+ for(i=0; i<TZSIZE; i++) {
+ if(rd_long(&p, &timezone.dlpairs[i]))
+ goto error;
+ if(timezone.dlpairs[i] == 0)
+ return;
+ }
+
+error:
+ timezone.stdiff = 0;
+ strcpy(timezone.stname, "GMT");
+ timezone.dlpairs[0] = 0;
+}
+
+static
+rd_name(char **f, char *p)
+{
+ int c, i;
+
+ for(;;) {
+ c = *(*f)++;
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ for(i=0; i<3; i++) {
+ if(c == ' ' || c == '\n')
+ return 1;
+ *p++ = c;
+ c = *(*f)++;
+ }
+ if(c != ' ' && c != '\n')
+ return 1;
+ *p = 0;
+ return 0;
+}
+
+static
+rd_long(char **f, long *p)
+{
+ int c, s;
+ long l;
+
+ s = 0;
+ for(;;) {
+ c = *(*f)++;
+ if(c == '-') {
+ s++;
+ continue;
+ }
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ if(c == 0) {
+ *p = 0;
+ return 0;
+ }
+ l = 0;
+ for(;;) {
+ if(c == ' ' || c == '\n')
+ break;
+ if(c < '0' || c > '9')
+ return 1;
+ l = l*10 + c-'0';
+ c = *(*f)++;
+ }
+ if(s)
+ l = -l;
+ *p = l;
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/cuserid.c b/sys/src/ape/lib/ap/plan9/cuserid.c
new file mode 100755
index 000000000..bf78bb3d2
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/cuserid.c
@@ -0,0 +1,21 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+/*
+ * BUG: supposed to be for effective uid,
+ * but plan9 doesn't have that concept
+ */
+char *
+cuserid(char *s)
+{
+ char *logname;
+ static char buf[L_cuserid];
+
+ if((logname = getlogin()) == NULL)
+ return(NULL);
+ if(s == 0)
+ s = buf;
+ strncpy(s, logname, sizeof buf);
+ return(s);
+}
diff --git a/sys/src/ape/lib/ap/plan9/dir.h b/sys/src/ape/lib/ap/plan9/dir.h
new file mode 100755
index 000000000..2af4f3e96
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/dir.h
@@ -0,0 +1,80 @@
+typedef long long vlong;
+typedef unsigned long long uvlong;
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+#define GBIT8(p) ((p)[0])
+#define GBIT16(p) ((p)[0]|((p)[1]<<8))
+#define GBIT32(p) ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
+#define GBIT64(p) ((vlong)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) |\
+ ((vlong)((p)[4]|((p)[5]<<8)|((p)[6]<<16)|((p)[7]<<24)) << 32))
+
+#define PBIT8(p,v) (p)[0]=(v)
+#define PBIT16(p,v) (p)[0]=(v);(p)[1]=(v)>>8
+#define PBIT32(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+#define PBIT64(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;\
+ (p)[4]=(v)>>32;(p)[5]=(v)>>40;(p)[6]=(v)>>48;(p)[7]=(v)>>56
+
+#define BIT8SZ 1
+#define BIT16SZ 2
+#define BIT32SZ 4
+#define BIT64SZ 8
+#define QIDSZ (BIT8SZ+BIT32SZ+BIT64SZ)
+
+/* STATFIXLEN includes leading 16-bit count */
+/* The count, however, excludes itself; total size is BIT16SZ+count */
+#define STATFIXLEN (BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+1*BIT64SZ) /* amount of fixed length data in a stat buffer */
+
+typedef union
+{
+ char clength[8];
+ vlong vlength;
+ struct
+ {
+ long hlength;
+ long length;
+ };
+} Length;
+
+typedef
+struct Qid
+{
+ uvlong path;
+ ulong vers;
+ uchar type;
+} Qid;
+
+typedef
+struct Dir {
+ /* system-modified data */
+ ushort type; /* server type */
+ uint dev; /* server subtype */
+ /* file data */
+ Qid qid; /* unique id from server */
+ ulong mode; /* permissions */
+ ulong atime; /* last read time */
+ ulong mtime; /* last write time */
+ vlong length; /* file length: see <u.h> */
+ char *name; /* last element of path */
+ char *uid; /* owner name */
+ char *gid; /* group name */
+ char *muid; /* last modifier name */
+} Dir;
+
+void _dirtostat(struct stat *, Dir*, Fdinfo*);
+uint _convM2D(uchar*, uint, Dir*, char*);
+uint _convD2M(Dir*, uchar*, uint);
+Dir *_dirstat(char*);
+int _dirwstat(char*, Dir*);
+Dir *_dirfstat(int);
+int _dirfwstat(int, Dir*);
+long _dirread(int, Dir**);
+long _dirreadall(int, Dir**);
+void _nulldir(Dir*);
+uint _sizeD2M(Dir*);
+
+#ifndef nil
+#define nil ((void*)0)
+#endif
diff --git a/sys/src/ape/lib/ap/plan9/dirread.c b/sys/src/ape/lib/ap/plan9/dirread.c
new file mode 100755
index 000000000..e03d32d77
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/dirread.c
@@ -0,0 +1,119 @@
+#include "lib.h"
+#include <string.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include "dir.h"
+
+static int
+statcheck(uchar *buf, uint nbuf)
+{
+ uchar *ebuf;
+ int i;
+
+ ebuf = buf + nbuf;
+
+ buf += STATFIXLEN - 4 * BIT16SZ;
+
+ for(i = 0; i < 4; i++){
+ if(buf + BIT16SZ > ebuf)
+ return -1;
+ buf += BIT16SZ + GBIT16(buf);
+ }
+
+ if(buf != ebuf)
+ return -1;
+
+ return 0;
+}
+
+static
+long
+dirpackage(uchar *buf, long ts, Dir **d)
+{
+ char *s;
+ long ss, i, n, nn, m;
+
+ if(ts == 0){
+ *d = nil;
+ return 0;
+ }
+
+ /*
+ * first find number of all stats, check they look like stats, & size all associated strings
+ */
+ ss = 0;
+ n = 0;
+ for(i = 0; i < ts; i += m){
+ m = BIT16SZ + GBIT16(&buf[i]);
+ if(statcheck(&buf[i], m) < 0)
+ break;
+ ss += m;
+ n++;
+ }
+
+ if(i != ts)
+ return -1;
+
+ *d = malloc(n * sizeof(Dir) + ss);
+ if(*d == nil)
+ return -1;
+
+ /*
+ * then convert all buffers
+ */
+ s = (char*)*d + n * sizeof(Dir);
+ nn = 0;
+ for(i = 0; i < ts; i += m){
+ m = BIT16SZ + GBIT16((uchar*)&buf[i]);
+ if(nn >= n || _convM2D(&buf[i], m, *d + nn, s) != m){
+ free(*d);
+ return -1;
+ }
+ nn++;
+ s += m;
+ }
+
+ return nn;
+}
+
+long
+_dirread(int fd, Dir **d)
+{
+ uchar *buf;
+ long ts;
+
+ buf = malloc(DIRMAX);
+ if(buf == nil)
+ return -1;
+ ts = _READ(fd, buf, DIRMAX);
+ if(ts >= 0)
+ ts = dirpackage(buf, ts, d);
+ free(buf);
+ return ts;
+}
+
+long
+_dirreadall(int fd, Dir **d)
+{
+ uchar *buf, *nbuf;
+ long n, ts;
+
+ buf = nil;
+ ts = 0;
+ for(;;){
+ nbuf = realloc(buf, ts+DIRMAX);
+ if(nbuf == nil){
+ free(buf);
+ return -1;
+ }
+ buf = nbuf;
+ n = _READ(fd, buf+ts, DIRMAX);
+ if(n <= 0)
+ break;
+ ts += n;
+ }
+ if(ts >= 0)
+ ts = dirpackage(buf, ts, d);
+ free(buf);
+ return ts;
+}
diff --git a/sys/src/ape/lib/ap/plan9/dirstat.c b/sys/src/ape/lib/ap/plan9/dirstat.c
new file mode 100755
index 000000000..f054b4f6e
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/dirstat.c
@@ -0,0 +1,107 @@
+#include "lib.h"
+#include <string.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include "dir.h"
+
+enum
+{
+ DIRSIZE = STATFIXLEN + 16 * 4 /* enough for encoded stat buf + some reasonable strings */
+};
+
+Dir*
+_dirstat(char *name)
+{
+ Dir *d;
+ uchar *buf;
+ int n, nd, i;
+
+ nd = DIRSIZE;
+ for(i=0; i<2; i++){ /* should work by the second try */
+ d = malloc(sizeof(Dir) + BIT16SZ +nd);
+ if(d == nil)
+ return nil;
+ buf = (uchar*)&d[1];
+ n = _STAT(name, buf, BIT16SZ+nd);
+ if(n < BIT16SZ){
+ free(d);
+ return nil;
+ }
+ nd = GBIT16((uchar*)buf); /* size needed to store whole stat buffer */
+ if(nd <= n){
+ _convM2D(buf, n, d, (char*)&d[1]);
+ return d;
+ }
+ /* else sizeof(Dir)+BIT16SZ+nd is plenty */
+ free(d);
+ }
+ return nil;
+}
+
+int
+_dirwstat(char *name, Dir *d)
+{
+ uchar *buf;
+ int r;
+
+ r = _sizeD2M(d);
+ buf = malloc(r);
+ if(buf == nil)
+ return -1;
+ _convD2M(d, buf, r);
+ r = _WSTAT(name, buf, r);
+ free(buf);
+ return r;
+}
+
+Dir*
+_dirfstat(int fd)
+{
+ Dir *d;
+ uchar *buf;
+ int n, nd, i;
+
+ nd = DIRSIZE;
+ for(i=0; i<2; i++){ /* should work by the second try */
+ d = malloc(sizeof(Dir) + nd);
+ if(d == nil)
+ return nil;
+ buf = (uchar*)&d[1];
+ n = _FSTAT(fd, buf, nd);
+ if(n < BIT16SZ){
+ free(d);
+ return nil;
+ }
+ nd = GBIT16(buf); /* size needed to store whole stat buffer */
+ if(nd <= n){
+ _convM2D(buf, n, d, (char*)&d[1]);
+ return d;
+ }
+ /* else sizeof(Dir)+nd is plenty */
+ free(d);
+ }
+ return nil;
+}
+
+int
+_dirfwstat(int fd, Dir *d)
+{
+ uchar *buf;
+ int r;
+
+ r = _sizeD2M(d);
+ buf = malloc(r);
+ if(buf == nil)
+ return -1;
+ _convD2M(d, buf, r);
+ r = _FWSTAT(fd, buf, r);
+ free(buf);
+ return r;
+}
+
+void
+_nulldir(Dir *d)
+{
+ memset(d, ~0, sizeof(Dir));
+ d->name = d->uid = d->gid = d->muid = "";
+}
diff --git a/sys/src/ape/lib/ap/plan9/dirtostat.c b/sys/src/ape/lib/ap/plan9/dirtostat.c
new file mode 100755
index 000000000..6abc6dfa7
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/dirtostat.c
@@ -0,0 +1,52 @@
+#include "lib.h"
+#include <sys/stat.h>
+#include <errno.h>
+#include "sys9.h"
+#include "dir.h"
+
+/* fi is non-null if there is an fd associated with s */
+void
+_dirtostat(struct stat *s, Dir *d, Fdinfo *fi)
+{
+ int num;
+ char *nam;
+
+ s->st_dev = (d->type<<8)|(d->dev&0xFF);
+ s->st_ino = d->qid.path;
+ s->st_mode = d->mode&0777;
+ if(fi && (fi->flags&FD_ISTTY))
+ s->st_mode |= S_IFCHR;
+ else if(d->mode & 0x80000000)
+ s->st_mode |= S_IFDIR;
+ else if(d->type == '|' || d->type == 's')
+ s->st_mode |= S_IFIFO;
+ else if(d->type != 'M')
+ s->st_mode |= S_IFCHR;
+ else
+ s->st_mode |= S_IFREG;
+ s->st_nlink = 1;
+ s->st_uid = 1;
+ s->st_gid = 1;
+ if(fi && (fi->flags&FD_BUFFERED))
+ s->st_size = fi->buf->n;
+ else
+ s->st_size = d->length;
+ s->st_atime = d->atime;
+ s->st_mtime = d->mtime;
+ s->st_ctime = d->mtime;
+ if(fi && fi->uid != -2){
+ s->st_uid = fi->uid;
+ s->st_gid = fi->gid;
+ } else {
+ nam = d->uid;
+ if(_getpw(&num, &nam, 0))
+ s->st_uid = num;
+ nam = d->gid;
+ if(_getpw(&num, &nam, 0))
+ s->st_gid = num;
+ if(fi){
+ fi->uid = s->st_uid;
+ fi->gid = s->st_gid;
+ }
+ }
+}
diff --git a/sys/src/ape/lib/ap/plan9/dup.c b/sys/src/ape/lib/ap/plan9/dup.c
new file mode 100755
index 000000000..37883be52
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/dup.c
@@ -0,0 +1,24 @@
+#include "lib.h"
+#include <unistd.h>
+#include <errno.h>
+
+int
+dup(int oldd)
+{
+ return fcntl(oldd, F_DUPFD, 0);
+}
+
+int
+dup2(int oldd, int newd)
+{
+ int n;
+
+ if(newd < 0 || newd >= OPEN_MAX){
+ errno = EBADF;
+ return -1;
+ }
+ if(oldd == newd && _fdinfo[newd].flags&FD_ISOPEN)
+ return newd;
+ close(newd);
+ return fcntl(oldd, F_DUPFD, newd);
+}
diff --git a/sys/src/ape/lib/ap/plan9/execl.c b/sys/src/ape/lib/ap/plan9/execl.c
new file mode 100755
index 000000000..79ffa44c3
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/execl.c
@@ -0,0 +1,9 @@
+#include <unistd.h>
+
+extern char **environ;
+
+int
+execl(const char *name, const char *arg0, ...)
+{
+ return execve(name, &arg0, environ);
+}
diff --git a/sys/src/ape/lib/ap/plan9/execle.c b/sys/src/ape/lib/ap/plan9/execle.c
new file mode 100755
index 000000000..f607c4914
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/execle.c
@@ -0,0 +1,11 @@
+#include <unistd.h>
+
+int
+execle(const char *name, const char *arg0, const char *aore, ...)
+{
+ char *p;
+
+ for(p=(char *)(&name)+1; *p; )
+ p++;
+ return execve(name, &arg0, (char **)p+1);
+}
diff --git a/sys/src/ape/lib/ap/plan9/execlp.c b/sys/src/ape/lib/ap/plan9/execlp.c
new file mode 100755
index 000000000..ff5d71761
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/execlp.c
@@ -0,0 +1,24 @@
+#include <unistd.h>
+#include <string.h>
+#include <sys/limits.h>
+
+/*
+ * BUG: instead of looking at PATH env variable,
+ * just try prepending /bin/ if name fails...
+ */
+
+extern char **environ;
+
+int
+execlp(const char *name, const char *arg0, ...)
+{
+ int n;
+ char buf[PATH_MAX];
+
+ if((n=execve(name, &arg0, environ)) < 0){
+ strcpy(buf, "/bin/");
+ strcpy(buf+5, name);
+ n = execve(buf, &name+1, environ);
+ }
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/execv.c b/sys/src/ape/lib/ap/plan9/execv.c
new file mode 100755
index 000000000..409e96003
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/execv.c
@@ -0,0 +1,9 @@
+#include <unistd.h>
+
+extern char **environ;
+
+int
+execv(const char *name, const char *argv[])
+{
+ return execve(name, argv, environ);
+}
diff --git a/sys/src/ape/lib/ap/plan9/execve.c b/sys/src/ape/lib/ap/plan9/execve.c
new file mode 100755
index 000000000..1d5c3c2fe
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/execve.c
@@ -0,0 +1,103 @@
+#include "lib.h"
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include "sys9.h"
+
+extern char **environ;
+
+int
+execve(const char *name, const char *argv[], const char *envp[])
+{
+ int n, f, i;
+ char **e, *ss, *se;
+ Fdinfo *fi;
+ unsigned long flags;
+ char nam[256+5];
+ char buf[1000];
+
+ _RFORK(RFCENVG);
+ /*
+ * To pass _fdinfo[] across exec, put lines like
+ * fd flags oflags
+ * in $_fdinfo (for open fd's)
+ */
+
+ f = _CREATE("#e/_fdinfo", OWRITE, 0666);
+ ss = buf;
+ for(n = 0; n<OPEN_MAX; n++){
+ fi = &_fdinfo[n];
+ flags = fi->flags;
+ if(flags&FD_CLOEXEC){
+ _CLOSE(n);
+ fi->flags = 0;
+ fi->oflags = 0;
+ }else if(flags&FD_ISOPEN){
+ ss = _ultoa(ss, n);
+ *ss++ = ' ';
+ ss = _ultoa(ss, flags);
+ *ss++ = ' ';
+ ss = _ultoa(ss, fi->oflags);
+ *ss++ = '\n';
+ if(ss-buf < sizeof(buf)-50){
+ _WRITE(f, buf, ss-buf);
+ ss = buf;
+ }
+ }
+ }
+ if(ss > buf)
+ _WRITE(f, buf, ss-buf);
+ _CLOSE(f);
+ /*
+ * To pass _sighdlr[] across exec, set $_sighdlr
+ * to list of blank separated fd's that have
+ * SIG_IGN (the rest will be SIG_DFL).
+ * We write the variable, even if no signals
+ * are ignored, in case the current value of the
+ * variable ignored some.
+ */
+ f = _CREATE("#e/_sighdlr", OWRITE, 0666);
+ if(f >= 0){
+ ss = buf;
+ for(i = 0, n=0; i <=MAXSIG && ss < &buf[sizeof(buf)]-5; i++) {
+ if(_sighdlr[i] == SIG_IGN) {
+ ss = _ultoa(ss, i);
+ *ss++ = ' ';
+ }
+ }
+ _WRITE(f, buf, ss-buf);
+ _CLOSE(f);
+ }
+ if(envp){
+ strcpy(nam, "#e/");
+ for(e = envp; (ss = *e); e++) {
+ se = strchr(ss, '=');
+ if(!se || ss==se)
+ continue; /* what is name? value? */
+ n = se-ss;
+ if(n >= sizeof(nam)-3)
+ n = sizeof(nam)-3-1;
+ memcpy(nam+3, ss, n);
+ nam[3+n] = 0;
+ f = _CREATE(nam, OWRITE, 0666);
+ if(f < 0)
+ continue;
+ se++; /* past = */
+ n = strlen(se);
+ /* temporarily decode nulls (see _envsetup()) */
+ for(i=0; i < n; i++)
+ if(se[i] == 1)
+ se[i] = 0;
+ _WRITE(f, se, n);
+ /* put nulls back */
+ for(i=0; i < n; i++)
+ if(se[i] == 0)
+ se[i] = 1;
+ _CLOSE(f);
+ }
+ }
+ n = _EXEC(name, argv);
+ _syserrno();
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/execvp.c b/sys/src/ape/lib/ap/plan9/execvp.c
new file mode 100755
index 000000000..f2ed53d1d
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/execvp.c
@@ -0,0 +1,24 @@
+#include <unistd.h>
+#include <sys/limits.h>
+#include <string.h>
+
+extern char **environ;
+
+/*
+ * BUG: instead of looking at PATH env variable,
+ * just try prepending /bin/ if name fails...
+ */
+
+int
+execvp(const char *name, const char **argv)
+{
+ int n;
+ char buf[PATH_MAX];
+
+ if((n=execve(name, argv, environ)) < 0){
+ strcpy(buf, "/bin/");
+ strcpy(buf+5, name);
+ n = execve(buf, argv, environ);
+ }
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/fcall.h b/sys/src/ape/lib/ap/plan9/fcall.h
new file mode 100755
index 000000000..8090a19a4
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/fcall.h
@@ -0,0 +1,118 @@
+typedef struct Fcall Fcall;
+
+/* see /sys/include/auth.h */
+enum
+{
+ DOMLEN= 48, /* length of an authentication domain name */
+ DESKEYLEN= 7, /* length of a des key for encrypt/decrypt */
+ CHALLEN= 8, /* length of a challenge */
+ NETCHLEN= 16, /* max network challenge length */
+ CONFIGLEN= 14,
+
+ KEYDBLEN= NAMELEN+DESKEYLEN+4+2
+};
+#define TICKETLEN (CHALLEN+2*NAMELEN+DESKEYLEN+1)
+#define AUTHENTLEN (CHALLEN+4+1)
+
+struct Fcall
+{
+ char type;
+ short fid;
+ unsigned short tag;
+ union
+ {
+ struct
+ {
+ unsigned short oldtag; /* T-Flush */
+ Qid qid; /* R-Attach, R-Walk, R-Open, R-Create */
+ char rauth[AUTHENTLEN]; /* Rattach */
+ };
+ struct
+ {
+ char uname[NAMELEN]; /* T-Attach */
+ char aname[NAMELEN]; /* T-Attach */
+ char ticket[TICKETLEN]; /* T-Attach */
+ char auth[AUTHENTLEN];/* T-Attach */
+ };
+ struct
+ {
+ char ename[ERRLEN]; /* R-Error */
+ char authid[NAMELEN]; /* R-session */
+ char authdom[DOMLEN]; /* R-session */
+ char chal[CHALLEN]; /* T-session/R-session */
+ };
+ struct
+ {
+ long perm; /* T-Create */
+ short newfid; /* T-Clone, T-Clwalk */
+ char name[NAMELEN]; /* T-Walk, T-Clwalk, T-Create */
+ char mode; /* T-Create, T-Open */
+ };
+ struct
+ {
+ long offset; /* T-Read, T-Write */
+ long count; /* T-Read, T-Write, R-Read */
+ char *data; /* T-Write, R-Read */
+ };
+ struct
+ {
+ char stat[DIRLEN]; /* T-Wstat, R-Stat */
+ };
+ };
+};
+
+#define MAXFDATA 8192
+#define MAXMSG 160 /* max header sans data */
+#define NOTAG 0xFFFF /* Dummy tag */
+
+enum
+{
+ Tmux = 48,
+ Rmux, /* illegal */
+ Tnop = 50,
+ Rnop,
+ Tosession = 52, /* illegal */
+ Rosession, /* illegal */
+ Terror = 54, /* illegal */
+ Rerror,
+ Tflush = 56,
+ Rflush,
+ Toattach = 58, /* illegal */
+ Roattach, /* illegal */
+ Tclone = 60,
+ Rclone,
+ Twalk = 62,
+ Rwalk,
+ Topen = 64,
+ Ropen,
+ Tcreate = 66,
+ Rcreate,
+ Tread = 68,
+ Rread,
+ Twrite = 70,
+ Rwrite,
+ Tclunk = 72,
+ Rclunk,
+ Tremove = 74,
+ Rremove,
+ Tstat = 76,
+ Rstat,
+ Twstat = 78,
+ Rwstat,
+ Tclwalk = 80,
+ Rclwalk,
+ Tauth = 82, /* illegal */
+ Rauth, /* illegal */
+ Tsession = 84,
+ Rsession,
+ Tattach = 86,
+ Rattach,
+};
+
+int convM2S(char*, Fcall*, int);
+int convS2M(Fcall*, char*);
+
+int convM2D(char*, Dir*);
+int convD2M(Dir*, char*);
+
+char* getS(int, char*, Fcall*, long*);
diff --git a/sys/src/ape/lib/ap/plan9/fcntl.c b/sys/src/ape/lib/ap/plan9/fcntl.c
new file mode 100755
index 000000000..053e38ec3
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/fcntl.c
@@ -0,0 +1,81 @@
+#include "lib.h"
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include "sys9.h"
+
+/*
+ * BUG: advisory locking not implemented
+ */
+
+#define OFL (O_ACCMODE|O_NONBLOCK|O_APPEND)
+
+int
+fcntl(int fd, int cmd, ...)
+{
+ int arg, i, ans, err;
+ Fdinfo *fi, *fans;
+ va_list va;
+ unsigned long oflags;
+
+ err = 0;
+ ans = 0;
+ va_start(va, cmd);
+ arg = va_arg(va, int);
+ va_end(va);
+ fi = &_fdinfo[fd];
+ if(fd<0 || fd>=OPEN_MAX || !(fi->flags&FD_ISOPEN))
+ err = EBADF;
+ else switch(cmd){
+ case F_DUPFD:
+ if(fi->flags&(FD_BUFFERED|FD_BUFFEREDX)){
+ err = EGREG; /* dup of buffered fd not implemented */
+ break;
+ }
+ oflags = fi->oflags;
+ for(i = (arg>0)? arg : 0; i<OPEN_MAX; i++)
+ if(!(_fdinfo[i].flags&FD_ISOPEN))
+ break;
+ if(i == OPEN_MAX)
+ err = EMFILE;
+ else {
+ ans = _DUP(fd, i);
+ if(ans != i){
+ if(ans < 0){
+ _syserrno();
+ err = errno;
+ }else
+ err = EBADF;
+ }else{
+ fans = &_fdinfo[ans];
+ fans->flags = fi->flags&~FD_CLOEXEC;
+ fans->oflags = oflags;
+ fans->uid = fi->uid;
+ fans->gid = fi->gid;
+ }
+ }
+ break;
+ case F_GETFD:
+ ans = fi->flags&FD_CLOEXEC;
+ break;
+ case F_SETFD:
+ fi->flags = (fi->flags&~FD_CLOEXEC)|(arg&FD_CLOEXEC);
+ break;
+ case F_GETFL:
+ ans = fi->oflags&OFL;
+ break;
+ case F_SETFL:
+ fi->oflags = (fi->oflags&~OFL)|(arg&OFL);
+ break;
+ case F_GETLK:
+ case F_SETLK:
+ case F_SETLKW:
+ err = EINVAL;
+ break;
+ }
+ if(err){
+ errno = err;
+ ans = -1;
+ }
+ return ans;
+}
diff --git a/sys/src/ape/lib/ap/plan9/fork.c b/sys/src/ape/lib/ap/plan9/fork.c
new file mode 100755
index 000000000..8137bc688
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/fork.c
@@ -0,0 +1,19 @@
+#include "lib.h"
+#include <errno.h>
+#include <unistd.h>
+#include "sys9.h"
+
+pid_t
+fork(void)
+{
+ int n;
+
+ n = _RFORK(RFENVG|RFFDG|RFPROC);
+ if(n < 0)
+ _syserrno();
+ if(n == 0) {
+ _detachbuf();
+ _sessleader = 0;
+ }
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/frexp.c b/sys/src/ape/lib/ap/plan9/frexp.c
new file mode 100755
index 000000000..588f90cee
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/frexp.c
@@ -0,0 +1,89 @@
+#include <math.h>
+#include <errno.h>
+#define _RESEARCH_SOURCE
+#include <float.h>
+
+#define MASK 0x7ffL
+#define SHIFT 20
+#define BIAS 1022L
+
+typedef union
+{
+ double d;
+ struct
+ {
+#ifdef IEEE_8087
+ long ls;
+ long ms;
+#else
+ long ms;
+ long ls;
+#endif
+ };
+} Cheat;
+
+double
+frexp(double d, int *ep)
+{
+ Cheat x;
+
+ if(d == 0) {
+ *ep = 0;
+ return 0;
+ }
+ x.d = d;
+ *ep = ((x.ms >> SHIFT) & MASK) - BIAS;
+ x.ms &= ~(MASK << SHIFT);
+ x.ms |= BIAS << SHIFT;
+ return x.d;
+}
+
+double
+ldexp(double d, int e)
+{
+ Cheat x;
+
+ if(d == 0)
+ return 0;
+ x.d = d;
+ e += (x.ms >> SHIFT) & MASK;
+ if(e <= 0)
+ return 0;
+ if(e >= MASK){
+ errno = ERANGE;
+ if(d < 0)
+ return -HUGE_VAL;
+ return HUGE_VAL;
+ }
+ x.ms &= ~(MASK << SHIFT);
+ x.ms |= (long)e << SHIFT;
+ return x.d;
+}
+
+double
+modf(double d, double *ip)
+{
+ double f;
+ Cheat x;
+ int e;
+
+ if(d < 1) {
+ if(d < 0) {
+ f = modf(-d, ip);
+ *ip = -*ip;
+ return -f;
+ }
+ *ip = 0;
+ return d;
+ }
+ x.d = d;
+ e = ((x.ms >> SHIFT) & MASK) - BIAS;
+ if(e <= SHIFT+1) {
+ x.ms &= ~(0x1fffffL >> e);
+ x.ls = 0;
+ } else
+ if(e <= SHIFT+33)
+ x.ls &= ~(0x7fffffffL >> (e-SHIFT-2));
+ *ip = x.d;
+ return d - x.d;
+}
diff --git a/sys/src/ape/lib/ap/plan9/fstat.c b/sys/src/ape/lib/ap/plan9/fstat.c
new file mode 100755
index 000000000..62e6c653a
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/fstat.c
@@ -0,0 +1,20 @@
+#include "lib.h"
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include "dir.h"
+
+int
+fstat(int fd, struct stat *buf)
+{
+ Dir *d;
+
+ if((d = _dirfstat(fd)) == nil){
+ _syserrno();
+ return -1;
+ }
+ _dirtostat(buf, d, &_fdinfo[fd]);
+ free(d);
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/fsync.c b/sys/src/ape/lib/ap/plan9/fsync.c
new file mode 100755
index 000000000..f37d35c04
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/fsync.c
@@ -0,0 +1,10 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+int
+fsync(int fd)
+{
+ errno = EINVAL;
+ return -1;
+}
diff --git a/sys/src/ape/lib/ap/plan9/ftruncate.c b/sys/src/ape/lib/ap/plan9/ftruncate.c
new file mode 100755
index 000000000..aa867488c
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/ftruncate.c
@@ -0,0 +1,23 @@
+#include "lib.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include "dir.h"
+
+int
+ftruncate(int fd, off_t length)
+{
+ Dir d;
+
+ if(length < 0){
+ errno = EINVAL;
+ return -1;
+ }
+ _nulldir(&d);
+ d.length = length;
+ if(_dirfwstat(fd, &d) < 0){
+ _syserrno();
+ return -1;
+ }
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/getcwd.c b/sys/src/ape/lib/ap/plan9/getcwd.c
new file mode 100755
index 000000000..db6936b56
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/getcwd.c
@@ -0,0 +1,32 @@
+#include "lib.h"
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include "sys9.h"
+#include "dir.h"
+
+char*
+getcwd(char *buf, size_t len)
+{
+ int fd;
+
+ fd = _OPEN(".", OREAD);
+ if(fd < 0) {
+ errno = EACCES;
+ return 0;
+ }
+ if(_FD2PATH(fd, buf, len) < 0) {
+ errno = EIO;
+ _CLOSE(fd);
+ return 0;
+ }
+ _CLOSE(fd);
+
+/* RSC: is this necessary? */
+ if(buf[0] == '\0')
+ strcpy(buf, "/");
+ return buf;
+}
diff --git a/sys/src/ape/lib/ap/plan9/getgid.c b/sys/src/ape/lib/ap/plan9/getgid.c
new file mode 100755
index 000000000..e1f5fc39b
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/getgid.c
@@ -0,0 +1,21 @@
+#include <sys/types.h>
+#include <grp.h>
+#include <unistd.h>
+
+/*
+ * BUG: assumes group that is same as user name
+ * is the one wanted (plan 9 has no "current group")
+ */
+gid_t
+getgid(void)
+{
+ struct group *g;
+ g = getgrnam(getlogin());
+ return g? g->gr_gid : 1;
+}
+
+gid_t
+getegid(void)
+{
+ return getgid();
+}
diff --git a/sys/src/ape/lib/ap/plan9/getgrgid.c b/sys/src/ape/lib/ap/plan9/getgrgid.c
new file mode 100755
index 000000000..77e2997fa
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/getgrgid.c
@@ -0,0 +1,25 @@
+#include <stddef.h>
+#include <grp.h>
+
+extern int _getpw(int *, char **, char **);
+extern char **_grpmems(char *);
+
+static struct group holdgroup;
+
+struct group *
+getgrgid(gid_t gid)
+{
+ int num;
+ char *nam, *mem;
+
+ num = gid;
+ nam = 0;
+ mem = 0;
+ if(_getpw(&num, &nam, &mem)){
+ holdgroup.gr_name = nam;
+ holdgroup.gr_gid = num;
+ holdgroup.gr_mem = _grpmems(mem);
+ return &holdgroup;
+ }
+ return NULL;
+}
diff --git a/sys/src/ape/lib/ap/plan9/getgrnam.c b/sys/src/ape/lib/ap/plan9/getgrnam.c
new file mode 100755
index 000000000..7033120e4
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/getgrnam.c
@@ -0,0 +1,25 @@
+#include <stddef.h>
+#include <grp.h>
+
+extern int _getpw(int *, char **, char **);
+extern char **_grpmems(char *);
+
+static struct group holdgroup;
+
+struct group *
+getgrnam(const char *name)
+{
+ int num;
+ char *nam, *mem;
+
+ num = 0;
+ nam = name;
+ mem = 0;
+ if(_getpw(&num, &nam, &mem)){
+ holdgroup.gr_name = nam;
+ holdgroup.gr_gid = num;
+ holdgroup.gr_mem = _grpmems(mem);
+ return &holdgroup;
+ }
+ return NULL;
+}
diff --git a/sys/src/ape/lib/ap/plan9/getgroups.c b/sys/src/ape/lib/ap/plan9/getgroups.c
new file mode 100755
index 000000000..e226a65b4
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/getgroups.c
@@ -0,0 +1,10 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+int
+getgroups(int gidsize, gid_t grouplist[])
+{
+ errno = EINVAL;
+ return -1;
+}
diff --git a/sys/src/ape/lib/ap/plan9/getlogin.c b/sys/src/ape/lib/ap/plan9/getlogin.c
new file mode 100755
index 000000000..8d2d51ce0
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/getlogin.c
@@ -0,0 +1,27 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/limits.h>
+
+char *
+getlogin_r(char *buf, int len)
+{
+ int f, n;
+
+ f = open("/dev/user", O_RDONLY);
+ if(f < 0)
+ return 0;
+ n = read(f, buf, len);
+ buf[len-1] = 0;
+ close(f);
+ return (n>=0)? buf : 0;
+}
+
+char *
+getlogin(void)
+{
+ static char buf[NAME_MAX+1];
+
+ return getlogin_r(buf, sizeof buf);
+}
diff --git a/sys/src/ape/lib/ap/plan9/getpgrp.c b/sys/src/ape/lib/ap/plan9/getpgrp.c
new file mode 100755
index 000000000..310028784
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/getpgrp.c
@@ -0,0 +1,27 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include "sys9.h"
+#include "lib.h"
+
+pid_t
+getpgrp(void)
+{
+ int n, f, pid;
+ char pgrpbuf[15], fname[30];
+
+ pid = getpid();
+ sprintf(fname, "/proc/%d/noteid", pid);
+ f = open(fname, 0);
+ n = read(f, pgrpbuf, sizeof pgrpbuf);
+ if(n < 0)
+ _syserrno();
+ else
+ n = atoi(pgrpbuf);
+ close(f);
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/getpid.c b/sys/src/ape/lib/ap/plan9/getpid.c
new file mode 100755
index 000000000..66eaaae8d
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/getpid.c
@@ -0,0 +1,22 @@
+#include "lib.h"
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include "sys9.h"
+
+pid_t
+getpid(void)
+{
+ int n, f;
+ char pidbuf[15];
+
+ f = _OPEN("#c/pid", 0);
+ n = _READ(f, pidbuf, sizeof pidbuf);
+ if(n < 0)
+ _syserrno();
+ else
+ n = atoi(pidbuf);
+ _CLOSE(f);
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/getppid.c b/sys/src/ape/lib/ap/plan9/getppid.c
new file mode 100755
index 000000000..01759221b
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/getppid.c
@@ -0,0 +1,23 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include "sys9.h"
+
+pid_t
+getppid(void)
+{
+ int n, f;
+ char ppidbuf[15];
+
+ f = open("#c/ppid", 0);
+ n = read(f, ppidbuf, sizeof ppidbuf);
+ if(n < 0)
+ errno = EINVAL;
+ else
+ n = atoi(ppidbuf);
+ close(f);
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/getpwnam.c b/sys/src/ape/lib/ap/plan9/getpwnam.c
new file mode 100755
index 000000000..e9d1c5b4e
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/getpwnam.c
@@ -0,0 +1,29 @@
+#include "lib.h"
+#include <stddef.h>
+#include <pwd.h>
+#include <string.h>
+
+static struct passwd holdpw;
+static char dirbuf[40] = "/usr/";
+static char *rc = "/bin/rc";
+
+struct passwd *
+getpwnam(const char *name)
+{
+ int num;
+ char *nam, *mem;
+
+ num = 0;
+ nam = name;
+ mem = 0;
+ if(_getpw(&num, &nam, &mem)){
+ holdpw.pw_name = nam;
+ holdpw.pw_uid = num;
+ holdpw.pw_gid = num;
+ strncpy(dirbuf+5, nam, sizeof(dirbuf)-6);
+ holdpw.pw_dir = dirbuf;
+ holdpw.pw_shell = rc;
+ return &holdpw;
+ }
+ return NULL;
+}
diff --git a/sys/src/ape/lib/ap/plan9/getpwuid.c b/sys/src/ape/lib/ap/plan9/getpwuid.c
new file mode 100755
index 000000000..e2983765b
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/getpwuid.c
@@ -0,0 +1,30 @@
+#include <stddef.h>
+#include <pwd.h>
+#include <string.h>
+
+extern int _getpw(int *, char **, char **);
+
+static struct passwd holdpw;
+static char dirbuf[40] = "/usr/";
+static char *rc = "/bin/rc";
+
+struct passwd *
+getpwuid(uid_t uid)
+{
+ int num;
+ char *nam, *mem;
+
+ num = uid;
+ nam = 0;
+ mem = 0;
+ if(_getpw(&num, &nam, &mem)){
+ holdpw.pw_name = nam;
+ holdpw.pw_uid = num;
+ holdpw.pw_gid = num;
+ strncpy(dirbuf+5, nam, sizeof(dirbuf)-6);
+ holdpw.pw_dir = dirbuf;
+ holdpw.pw_shell = rc;
+ return &holdpw;
+ }
+ return NULL;
+}
diff --git a/sys/src/ape/lib/ap/plan9/getuid.c b/sys/src/ape/lib/ap/plan9/getuid.c
new file mode 100755
index 000000000..05d4c0832
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/getuid.c
@@ -0,0 +1,17 @@
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
+
+uid_t
+getuid(void)
+{
+ struct passwd *p;
+ p = getpwnam(getlogin());
+ return p? p->pw_uid : 1;
+}
+
+uid_t
+geteuid(void)
+{
+ return getuid();
+}
diff --git a/sys/src/ape/lib/ap/plan9/isatty.c b/sys/src/ape/lib/ap/plan9/isatty.c
new file mode 100755
index 000000000..e04cd891d
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/isatty.c
@@ -0,0 +1,29 @@
+#include "lib.h"
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sys9.h"
+#include "dir.h"
+
+int
+_isatty(int fd)
+{
+ int t;
+ char buf[64];
+
+ if(_FD2PATH(fd, buf, sizeof buf) < 0)
+ return 0;
+
+ /* might be /mnt/term/dev/cons */
+ return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
+}
+
+/* The FD_ISTTY flag is set via _isatty in _fdsetup or open */
+int
+isatty(fd)
+{
+ if(_fdinfo[fd].flags&FD_ISTTY)
+ return 1;
+ else
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/kill.c b/sys/src/ape/lib/ap/plan9/kill.c
new file mode 100755
index 000000000..95bbbdcc7
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/kill.c
@@ -0,0 +1,60 @@
+#include "lib.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+static int
+note(int pid, char *msg, char *fmt)
+{
+ int f;
+ char pname[50];
+
+ sprintf(pname, fmt, pid);
+ f = open(pname, O_WRONLY);
+ if(f < 0){
+ errno = ESRCH;
+ return -1;
+ }
+ if(msg != 0 && write(f, msg, strlen(msg)) < 0){
+ close(f);
+ errno = EPERM;
+ return -1;
+ }
+ close(f);
+ return 0;
+}
+
+int
+kill(pid_t pid, int sig)
+{
+ char *msg;
+ int sid, r, mpid;
+
+ if(sig == 0)
+ msg = 0;
+ else {
+ msg = _sigstring(sig);
+ if(msg == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ if(pid < 0) {
+ sid = getpgrp();
+ mpid = getpid();
+ if(setpgid(mpid, -pid) == 0) {
+ r = note(mpid, msg, "/proc/%d/notepg");
+ setpgid(mpid, sid);
+ } else {
+ r = -1;
+ }
+ } else if(pid == 0)
+ r = note(getpid(), msg, "/proc/%d/notepg");
+ else
+ r = note(pid, msg, "/proc/%d/note");
+ return r;
+}
diff --git a/sys/src/ape/lib/ap/plan9/lib.h b/sys/src/ape/lib/ap/plan9/lib.h
new file mode 100755
index 000000000..91a51d5d3
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/lib.h
@@ -0,0 +1,72 @@
+#include <sys/types.h>
+#include <sys/limits.h>
+#include <fcntl.h>
+#include <ureg.h>
+
+typedef struct Ureg Ureg;
+
+/* mux buf for selecting (see _buf.c) */
+enum {
+ READMAX = 8192, /* read at most this much with _READ */
+ PERFDMAX = 2*READMAX, /* stop _READing an fd when it has this much */
+ INITBUFS = 4, /* allow enough room for this many PERFDMAX */
+};
+
+typedef struct Muxbuf {
+ int n; /* # unprocessed chars in buf */
+ unsigned char* putnext; /* place for copy process to put next data */
+ unsigned char* getnext; /* place for parent process to get next data */
+ char fd; /* fd for which this is a buffer */
+ unsigned char eof; /* true if eof after current data exhausted */
+ unsigned char roomwait; /* true if copy process is waiting for room */
+ unsigned char datawait; /* true if parent process is waiting for data */
+ int copypid; /* pid of copyproc */
+ unsigned char data[PERFDMAX];
+} Muxbuf;
+
+/* be sure to change _fdinfo[] init in _fdinfo if you change this */
+typedef struct Fdinfo{
+ unsigned long flags;
+ unsigned long oflags;
+ uid_t uid;
+ gid_t gid;
+ char *name;
+ /*
+ * the following is used if flags&FD_BUFFERED
+ */
+ Muxbuf *buf; /* holds buffered data and state */
+} Fdinfo;
+
+/* #define FD_CLOEXEC 1 is in fcntl.h */
+
+#define FD_ISOPEN 0x2
+#define FD_BUFFERED 0x4
+#define FD_BUFFEREDX 0x8
+#define FD_ISTTY 0x20
+
+#define MAXSIG SIGUSR2
+
+extern Fdinfo _fdinfo[];
+
+extern int _finishing;
+extern int _sessleader;
+extern void (*_sighdlr[])(int, char*, Ureg*);
+extern char *_sigstring(int);
+extern int _stringsig(char *);
+extern long _psigblocked;
+extern int _startbuf(int);
+extern int _selbuf(int);
+extern void _closebuf(int);
+extern int _readbuf(int, void*, int, int);
+extern void _detachbuf(void);
+extern void _finish(int, char *);
+extern char *_ultoa(char *, unsigned long);
+extern int _notehandler(void *, char *);
+extern void _notetramp(int, void (*)(int, char*, Ureg*), Ureg*);
+extern void _syserrno(void);
+extern int _getpw(int *, char **, char **);
+extern int _isatty(int);
+extern void _fdinit(char*, char*);
+
+
+void checkbug(char *, int);
diff --git a/sys/src/ape/lib/ap/plan9/link.c b/sys/src/ape/lib/ap/plan9/link.c
new file mode 100755
index 000000000..9bf3755fb
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/link.c
@@ -0,0 +1,12 @@
+#include <unistd.h>
+#include <errno.h>
+
+/*
+ * BUG: LINK_MAX==1 isn't really allowed
+ */
+int
+link(const char *name1, const char *name2)
+{
+ errno = EMLINK;
+ return -1;
+}
diff --git a/sys/src/ape/lib/ap/plan9/lseek.c b/sys/src/ape/lib/ap/plan9/lseek.c
new file mode 100755
index 000000000..8c4eba39d
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/lseek.c
@@ -0,0 +1,24 @@
+#include "lib.h"
+#include <unistd.h>
+#include <errno.h>
+#include "sys9.h"
+
+/*
+ * BUG: errno mapping
+ */
+off_t
+lseek(int d, off_t offset, int whence)
+{
+ long long n;
+ int flags;
+
+ flags = _fdinfo[d].flags;
+ if(flags&(FD_BUFFERED|FD_BUFFEREDX|FD_ISTTY)) {
+ errno = ESPIPE;
+ return -1;
+ }
+ n = _SEEK(d, offset, whence);
+ if(n < 0)
+ _syserrno();
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/malloc.c b/sys/src/ape/lib/ap/plan9/malloc.c
new file mode 100755
index 000000000..09f3c178e
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/malloc.c
@@ -0,0 +1,142 @@
+#include <stdlib.h>
+#include <string.h>
+
+typedef unsigned int uint;
+
+enum
+{
+ MAGIC = 0xbada110c,
+ MAX2SIZE = 32,
+ CUTOFF = 12,
+};
+
+typedef struct Bucket Bucket;
+struct Bucket
+{
+ int size;
+ int magic;
+ Bucket *next;
+ int pad;
+ char data[1];
+};
+
+typedef struct Arena Arena;
+struct Arena
+{
+ Bucket *btab[MAX2SIZE];
+};
+static Arena arena;
+
+#define datoff ((int)((Bucket*)0)->data)
+#define nil ((void*)0)
+
+extern void *sbrk(unsigned long);
+
+void*
+malloc(size_t size)
+{
+ uint next;
+ int pow, n;
+ Bucket *bp, *nbp;
+
+ for(pow = 1; pow < MAX2SIZE; pow++) {
+ if(size <= (1<<pow))
+ goto good;
+ }
+
+ return nil;
+good:
+ /* Allocate off this list */
+ bp = arena.btab[pow];
+ if(bp) {
+ arena.btab[pow] = bp->next;
+
+ if(bp->magic != 0)
+ abort();
+
+ bp->magic = MAGIC;
+ return bp->data;
+ }
+ size = sizeof(Bucket)+(1<<pow);
+ size += 7;
+ size &= ~7;
+
+ if(pow < CUTOFF) {
+ n = (CUTOFF-pow)+2;
+ bp = sbrk(size*n);
+ if((int)bp < 0)
+ return nil;
+
+ next = (uint)bp+size;
+ nbp = (Bucket*)next;
+ arena.btab[pow] = nbp;
+ for(n -= 2; n; n--) {
+ next = (uint)nbp+size;
+ nbp->next = (Bucket*)next;
+ nbp->size = pow;
+ nbp = nbp->next;
+ }
+ nbp->size = pow;
+ }
+ else {
+ bp = sbrk(size);
+ if((int)bp < 0)
+ return nil;
+ }
+
+ bp->size = pow;
+ bp->magic = MAGIC;
+
+ return bp->data;
+}
+
+void
+free(void *ptr)
+{
+ Bucket *bp, **l;
+
+ if(ptr == nil)
+ return;
+
+ /* Find the start of the structure */
+ bp = (Bucket*)((uint)ptr - datoff);
+
+ if(bp->magic != MAGIC)
+ abort();
+
+ bp->magic = 0;
+ l = &arena.btab[bp->size];
+ bp->next = *l;
+ *l = bp;
+}
+
+void*
+realloc(void *ptr, size_t n)
+{
+ void *new;
+ uint osize;
+ Bucket *bp;
+
+ if(ptr == nil)
+ return malloc(n);
+
+ /* Find the start of the structure */
+ bp = (Bucket*)((uint)ptr - datoff);
+
+ if(bp->magic != MAGIC)
+ abort();
+
+ /* enough space in this bucket */
+ osize = 1<<bp->size;
+ if(osize >= n)
+ return ptr;
+
+ new = malloc(n);
+ if(new == nil)
+ return nil;
+
+ memmove(new, ptr, osize);
+ free(ptr);
+
+ return new;
+}
diff --git a/sys/src/ape/lib/ap/plan9/mkdir.c b/sys/src/ape/lib/ap/plan9/mkdir.c
new file mode 100755
index 000000000..1773150b5
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/mkdir.c
@@ -0,0 +1,27 @@
+#include "lib.h"
+#include <sys/stat.h>
+#include <errno.h>
+#include "sys9.h"
+
+/*
+ * BUG: errno mapping
+ */
+int
+mkdir(const char *name, mode_t mode)
+{
+ int n;
+ struct stat st;
+
+ if(stat(name, &st)==0) {
+ errno = EEXIST;
+ return -1;
+ }
+ n = _CREATE(name, 0, 0x80000000|(mode&0777));
+ if(n < 0)
+ _syserrno();
+ else{
+ _CLOSE(n);
+ n = 0;
+ }
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/mkfile b/sys/src/ape/lib/ap/plan9/mkfile
new file mode 100755
index 000000000..502d00f8b
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/mkfile
@@ -0,0 +1,110 @@
+APE=/sys/src/ape
+<$APE/config
+LIB=/$objtype/lib/ape/libap.a
+OFILES=\
+ _buf.$O\
+ _dirconv.$O\
+ _envsetup.$O\
+ _errno.$O\
+ _exit.$O\
+ _fdinfo.$O\
+ _getpw.$O\
+ _nap.$O\
+ 9mallocz.$O\
+ 9iounit.$O\
+ 9read.$O\
+ 9readn.$O\
+ 9wait.$O\
+ 9write.$O\
+ access.$O\
+ alarm.$O\
+ brk.$O\
+ cfgetospeed.$O\
+ chdir.$O\
+ chmod.$O\
+ chown.$O\
+ close.$O\
+ convM2D.$O\
+ convD2M.$O\
+ creat.$O\
+ ctermid.$O\
+ ctime.$O\
+ cuserid.$O\
+ dirread.$O\
+ dirstat.$O\
+ dirtostat.$O\
+ dup.$O\
+ execl.$O\
+ execle.$O\
+ execlp.$O\
+ execv.$O\
+ execve.$O\
+ execvp.$O\
+ fcntl.$O\
+ fork.$O\
+ frexp.$O\
+ fstat.$O\
+ fsync.$O\
+ ftruncate.$O\
+ getcwd.$O\
+ getgid.$O\
+ getgrgid.$O\
+ getgrnam.$O\
+ getgroups.$O\
+ getlogin.$O\
+ getpgrp.$O\
+ getpid.$O\
+ getppid.$O\
+ getpwnam.$O\
+ getpwuid.$O\
+ getuid.$O\
+ isatty.$O\
+ kill.$O\
+ link.$O\
+ lseek.$O\
+ malloc.$O\
+ mkdir.$O\
+ nan.$O\
+ open.$O\
+ opendir.$O\
+ pause.$O\
+ pipe.$O\
+ profile.$O\
+ qlock.$O\
+ read.$O\
+ rename.$O\
+ rmdir.$O\
+ setgid.$O\
+ setpgid.$O\
+ setsid.$O\
+ setuid.$O\
+ signal.$O\
+ sigpending.$O\
+ sigprocmask.$O\
+ sigsuspend.$O\
+ sleep.$O\
+ sqrt.$O\
+ stat.$O\
+ system.$O\
+ tcgetattr.$O\
+ time.$O\
+ times.$O\
+ tmpfile.$O\
+ ttyname.$O\
+ umask.$O\
+ uname.$O\
+ unlink.$O\
+ utime.$O\
+ wait.$O\
+ write.$O\
+
+UPDATE=\
+ mkfile\
+ /386/lib/ape/libap.a\
+ ${OFILES:%.$O=%.c}\
+
+</sys/src/cmd/mksyslib
+
+CFLAGS=-c -D_POSIX_SOURCE -D_PLAN9_SOURCE -D_BSD_EXTENSION
+
+$OFILES: lib.h
diff --git a/sys/src/ape/lib/ap/plan9/nan.c b/sys/src/ape/lib/ap/plan9/nan.c
new file mode 100755
index 000000000..db7c13fe8
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/nan.c
@@ -0,0 +1,54 @@
+#include <float.h>
+#include <math.h>
+
+#define NANEXP (2047<<20)
+#define NANMASK (2047<<20)
+#define NANSIGN (1<<31)
+
+double
+NaN(void)
+{
+ FPdbleword a;
+
+ a.hi = NANEXP;
+ a.lo = 1;
+ return a.x;
+}
+
+int
+isNaN(double d)
+{
+ FPdbleword a;
+
+ a.x = d;
+ if((a.hi & NANMASK) != NANEXP)
+ return 0;
+ return !isInf(d, 0);
+}
+
+double
+Inf(int sign)
+{
+ FPdbleword a;
+
+ a.hi = NANEXP;
+ a.lo = 0;
+ if(sign < 0)
+ a.hi |= NANSIGN;
+ return a.x;
+}
+
+int
+isInf(double d, int sign)
+{
+ FPdbleword a;
+
+ a.x = d;
+ if(a.lo != 0)
+ return 0;
+ if(a.hi == NANEXP)
+ return sign >= 0;
+ if(a.hi == (NANEXP|NANSIGN))
+ return sign <= 0;
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/open.c b/sys/src/ape/lib/ap/plan9/open.c
new file mode 100755
index 000000000..1cfe0d6b5
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/open.c
@@ -0,0 +1,62 @@
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "lib.h"
+#include <sys/stat.h>
+#include "sys9.h"
+
+/*
+ * O_NOCTTY has no effect
+ */
+int
+open(const char *path, int flags, ...)
+{
+ int n;
+ long f;
+ int mode;
+ Fdinfo *fi;
+ va_list va;
+ struct stat sbuf;
+
+ f = flags&O_ACCMODE;
+ if(flags&O_CREAT){
+ if(access(path, 0) >= 0){
+ if(flags&O_EXCL){
+ errno = EEXIST;
+ return -1;
+ }else{
+ if((flags&O_TRUNC)&&(flags&(O_RDWR|O_WRONLY)))
+ f |= 16;
+ n = _OPEN(path, f);
+ }
+ }else{
+ va_start(va, flags);
+ mode = va_arg(va, int);
+ va_end(va);
+ n = _CREATE(path, f, mode&0777);
+ }
+ if(n < 0)
+ _syserrno();
+ }else{
+ if((flags&O_TRUNC)&&(flags&(O_RDWR|O_WRONLY)))
+ f |= 16;
+ n = _OPEN(path, f);
+ if(n < 0)
+ _syserrno();
+ }
+ if(n >= 0){
+ fi = &_fdinfo[n];
+ fi->flags = FD_ISOPEN;
+ fi->oflags = flags&(O_ACCMODE|O_NONBLOCK|O_APPEND);
+ fi->uid = -2;
+ fi->gid = -2;
+ fi->name = malloc(strlen(path)+1);
+ if(fi->name)
+ strcpy(fi->name, path);
+ if(fi->oflags&O_APPEND)
+ _SEEK(n, 0, 2);
+ }
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/opendir.c b/sys/src/ape/lib/ap/plan9/opendir.c
new file mode 100755
index 000000000..ebc22dbf6
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/opendir.c
@@ -0,0 +1,121 @@
+#include "lib.h"
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include "sys9.h"
+#include "dir.h"
+
+#define DBLOCKSIZE 20
+
+DIR *
+opendir(const char *filename)
+{
+ int f;
+ DIR *d;
+ struct stat sb;
+ Dir *d9;
+
+ if((d9 = _dirstat(filename)) == nil){
+ _syserrno();
+ return NULL;
+ }
+ _dirtostat(&sb, d9, 0);
+ free(d9);
+ if(S_ISDIR(sb.st_mode) == 0) {
+ errno = ENOTDIR;
+ return NULL;
+ }
+
+ f = open(filename, O_RDONLY);
+ if(f < 0){
+ _syserrno();
+ return NULL;
+ }
+ _fdinfo[f].flags |= FD_CLOEXEC;
+ d = (DIR *)malloc(sizeof(DIR) + DBLOCKSIZE*sizeof(struct dirent));
+ if(!d){
+ errno = ENOMEM;
+ return NULL;
+ }
+ d->dd_buf = (char *)d + sizeof(DIR);
+ d->dd_fd = f;
+ d->dd_loc = 0;
+ d->dd_size = 0;
+ d->dirs = nil;
+ d->dirsize = 0;
+ d->dirloc = 0;
+ return d;
+}
+
+int
+closedir(DIR *d)
+{
+ if(!d){
+ errno = EBADF;
+ return -1;
+ }
+ if(close(d->dd_fd) < 0)
+ return -1;
+ free(d->dirs);
+ free(d);
+ return 0;
+}
+
+void
+rewinddir(DIR *d)
+{
+ if(!d)
+ return;
+ d->dd_loc = 0;
+ d->dd_size = 0;
+ d->dirsize = 0;
+ d->dirloc = 0;
+ free(d->dirs);
+ d->dirs = nil;
+ if(_SEEK(d->dd_fd, 0, 0) < 0){
+ _syserrno();
+ return;
+ }
+}
+
+struct dirent *
+readdir(DIR *d)
+{
+ int i, n;
+ struct dirent *dr;
+ Dir *dirs;
+
+ if(!d){
+ errno = EBADF;
+ return NULL;
+ }
+ if(d->dd_loc >= d->dd_size){
+ if(d->dirloc >= d->dirsize){
+ free(d->dirs);
+ d->dirs = NULL;
+ d->dirsize = _dirread(d->dd_fd, &d->dirs);
+ d->dirloc = 0;
+ }
+ if(d->dirsize < 0) { /* malloc or read failed in _dirread? */
+ free(d->dirs);
+ d->dirs = NULL;
+ }
+ if(d->dirs == NULL)
+ return NULL;
+
+ dr = (struct dirent *)d->dd_buf;
+ dirs = d->dirs;
+ for(i=0; i<DBLOCKSIZE && d->dirloc < d->dirsize; i++){
+ strncpy(dr[i].d_name, dirs[d->dirloc++].name, MAXNAMLEN);
+ dr[i].d_name[MAXNAMLEN] = 0;
+ }
+ d->dd_loc = 0;
+ d->dd_size = i*sizeof(struct dirent);
+ }
+ dr = (struct dirent*)(d->dd_buf+d->dd_loc);
+ d->dd_loc += sizeof(struct dirent);
+ return dr;
+}
diff --git a/sys/src/ape/lib/ap/plan9/pause.c b/sys/src/ape/lib/ap/plan9/pause.c
new file mode 100755
index 000000000..38fd96162
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/pause.c
@@ -0,0 +1,11 @@
+#include "lib.h"
+#include <unistd.h>
+#include "sys9.h"
+
+int
+pause(void)
+{
+ for(;;)
+ if(_SLEEP(1000*1000) < 0)
+ return;
+}
diff --git a/sys/src/ape/lib/ap/plan9/pipe.c b/sys/src/ape/lib/ap/plan9/pipe.c
new file mode 100755
index 000000000..edc2372e8
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/pipe.c
@@ -0,0 +1,31 @@
+#include <errno.h>
+#include "lib.h"
+#include "sys9.h"
+
+int
+pipe(int fildes[2])
+{
+ Fdinfo *fi;
+ int i;
+
+ if(!fildes){
+ errno = EFAULT;
+ return -1;
+ }
+ if(_PIPE(fildes) < 0)
+ _syserrno();
+ else
+ if(fildes[0] < 0 || fildes[0]>=OPEN_MAX ||
+ fildes[1] < 0 || fildes[1]>=OPEN_MAX) {
+ errno = EMFILE;
+ return -1;
+ }
+ for(i = 0; i <=1; i++) {
+ fi = &_fdinfo[fildes[i]];
+ fi->flags = FD_ISOPEN;
+ fi->oflags = O_RDWR;
+ fi->uid = 0; /* none */
+ fi->gid = 0;
+ }
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/profile.c b/sys/src/ape/lib/ap/plan9/profile.c
new file mode 100755
index 000000000..a69e65f2b
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/profile.c
@@ -0,0 +1,324 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include "sys9.h"
+
+enum {
+ Profoff, /* No profiling */
+ Profuser, /* Measure user time only (default) */
+ Profkernel, /* Measure user + kernel time */
+ Proftime, /* Measure total time */
+ Profsample, /* Use clock interrupt to sample (default when there is no cycle counter) */
+}; /* what */
+
+typedef long long vlong;
+typedef unsigned long ulong;
+typedef unsigned long long uvlong;
+
+#include "/sys/include/tos.h"
+
+extern void* sbrk(ulong);
+extern long _callpc(void**);
+extern long _savearg(void);
+extern void _cycles(uvlong*); /* 64-bit value of the cycle counter if there is one, 0 if there isn't */
+
+static ulong khz;
+static ulong perr;
+static int havecycles;
+
+typedef struct Plink Plink;
+struct Plink
+{
+ Plink *old;
+ Plink *down;
+ Plink *link;
+ long pc;
+ long count;
+ vlong time;
+};
+
+#pragma profile off
+
+ulong
+_profin(void)
+{
+ void *dummy;
+ long pc;
+ Plink *pp, *p;
+ ulong arg;
+ vlong t;
+
+ arg = _savearg();
+ pc = _callpc(&dummy);
+ pp = _tos->prof.pp;
+ if(pp == 0 || (_tos->prof.pid && _tos->pid != _tos->prof.pid))
+ return arg;
+
+ for(p=pp->down; p; p=p->link)
+ if(p->pc == pc)
+ goto out;
+ p = _tos->prof.next + 1;
+ if(p >= _tos->prof.last){
+ _tos->prof.pp = 0;
+ perr++;
+ return arg;
+ }
+ _tos->prof.next = p;
+ p->link = pp->down;
+ pp->down = p;
+ p->pc = pc;
+ p->old = pp;
+ p->down = 0;
+ p->count = 0;
+ p->time = 0LL;
+
+out:
+ _tos->prof.pp = p;
+ p->count++;
+ switch(_tos->prof.what){
+ case Profkernel:
+ p->time = p->time - _tos->pcycles;
+ goto proftime;
+ case Profuser:
+ /* Add kernel cycles on proc entry */
+ p->time = p->time + _tos->kcycles;
+ /* fall through */
+ case Proftime:
+ proftime: /* Subtract cycle counter on proc entry */
+ _cycles((uvlong*)&t);
+ p->time = p->time - t;
+ break;
+ case Profsample:
+ p->time = p->time - _tos->clock;
+ break;
+ }
+ return arg; /* disgusting linkage */
+}
+
+ulong
+_profout(void)
+{
+ Plink *p;
+ ulong arg;
+ vlong t;
+
+ arg = _savearg();
+ p = _tos->prof.pp;
+ if (p == NULL || (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid))
+ return arg; /* Not our process */
+ switch(_tos->prof.what){
+ case Profkernel: /* Add proc cycles on proc entry */
+ p->time = p->time + _tos->pcycles;
+ goto proftime;
+ case Profuser: /* Subtract kernel cycles on proc entry */
+ p->time = p->time - _tos->kcycles;
+ /* fall through */
+ case Proftime:
+ proftime: /* Add cycle counter on proc entry */
+ _cycles((uvlong*)&t);
+ p->time = p->time + t;
+ break;
+ case Profsample:
+ p->time = p->time + _tos->clock;
+ break;
+ }
+ _tos->prof.pp = p->old;
+ return arg;
+}
+
+/* stdio may not be ready for us yet */
+static void
+err(char *fmt, ...)
+{
+ int fd;
+ va_list arg;
+ char buf[128];
+
+ if((fd = open("/dev/cons", OWRITE)) == -1)
+ return;
+ va_start(arg, fmt);
+ /*
+ * C99 now requires *snprintf to return the number of characters
+ * that *would* have been emitted, had there been room for them,
+ * or a negative value on an `encoding error'. Arrgh!
+ */
+ vsnprintf(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+ write(fd, buf, strlen(buf));
+ close(fd);
+}
+
+void
+_profdump(void)
+{
+ int f;
+ long n;
+ Plink *p;
+ char *vp;
+ char filename[64];
+
+ if (_tos->prof.what == 0)
+ return; /* No profiling */
+ if (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid)
+ return; /* Not our process */
+ if(perr)
+ err("%lud Prof errors\n", perr);
+ _tos->prof.pp = NULL;
+ if (_tos->prof.pid)
+ snprintf(filename, sizeof filename - 1, "prof.%ld", _tos->prof.pid);
+ else
+ snprintf(filename, sizeof filename - 1, "prof.out");
+ f = creat(filename, 0666);
+ if(f < 0) {
+ err("%s: cannot create - %s\n", filename, strerror(errno));
+ return;
+ }
+ _tos->prof.pid = ~0; /* make sure data gets dumped once */
+ switch(_tos->prof.what){
+ case Profkernel:
+ _cycles((uvlong*)&_tos->prof.first->time);
+ _tos->prof.first->time = _tos->prof.first->time + _tos->pcycles;
+ break;
+ case Profuser:
+ _cycles((uvlong*)&_tos->prof.first->time);
+ _tos->prof.first->time = _tos->prof.first->time - _tos->kcycles;
+ break;
+ case Proftime:
+ _cycles((uvlong*)&_tos->prof.first->time);
+ break;
+ case Profsample:
+ _tos->prof.first->time = _tos->clock;
+ break;
+ }
+ vp = (char*)_tos->prof.first;
+
+ for(p = _tos->prof.first; p <= _tos->prof.next; p++) {
+
+ /*
+ * short down
+ */
+ n = 0xffff;
+ if(p->down)
+ n = p->down - _tos->prof.first;
+ vp[0] = n>>8;
+ vp[1] = n;
+
+ /*
+ * short right
+ */
+ n = 0xffff;
+ if(p->link)
+ n = p->link - _tos->prof.first;
+ vp[2] = n>>8;
+ vp[3] = n;
+ vp += 4;
+
+ /*
+ * long pc
+ */
+ n = p->pc;
+ vp[0] = n>>24;
+ vp[1] = n>>16;
+ vp[2] = n>>8;
+ vp[3] = n;
+ vp += 4;
+
+ /*
+ * long count
+ */
+ n = p->count;
+ vp[0] = n>>24;
+ vp[1] = n>>16;
+ vp[2] = n>>8;
+ vp[3] = n;
+ vp += 4;
+
+ /*
+ * vlong time
+ */
+ if (havecycles){
+ n = (vlong)(p->time / (vlong)khz);
+ }else
+ n = p->time;
+
+ vp[0] = n>>24;
+ vp[1] = n>>16;
+ vp[2] = n>>8;
+ vp[3] = n;
+ vp += 4;
+ }
+ write(f, (char*)_tos->prof.first, vp - (char*)_tos->prof.first);
+ close(f);
+
+}
+
+void
+_profinit(int entries, int what)
+{
+ if (_tos->prof.what == 0)
+ return; /* Profiling not linked in */
+ _tos->prof.pp = NULL;
+ _tos->prof.first = calloc(entries*sizeof(Plink),1);
+ _tos->prof.last = _tos->prof.first + entries;
+ _tos->prof.next = _tos->prof.first;
+ _tos->prof.pid = _tos->pid;
+ _tos->prof.what = what;
+ _tos->clock = 1;
+}
+
+void
+_profmain(void)
+{
+ char ename[50];
+ int n, f;
+
+ n = 2000;
+ if (_tos->cyclefreq != 0LL){
+ khz = _tos->cyclefreq / 1000; /* Report times in milliseconds */
+ havecycles = 1;
+ }
+ f = open("/env/profsize", OREAD);
+ if(f >= 0) {
+ memset(ename, 0, sizeof(ename));
+ read(f, ename, sizeof(ename)-1);
+ close(f);
+ n = atol(ename);
+ }
+ _tos->prof.what = Profuser;
+ f = open("/env/proftype", OREAD);
+ if(f >= 0) {
+ memset(ename, 0, sizeof(ename));
+ read(f, ename, sizeof(ename)-1);
+ close(f);
+ if (strcmp(ename, "user") == 0)
+ _tos->prof.what = Profuser;
+ else if (strcmp(ename, "kernel") == 0)
+ _tos->prof.what = Profkernel;
+ else if (strcmp(ename, "elapsed") == 0 || strcmp(ename, "time") == 0)
+ _tos->prof.what = Proftime;
+ else if (strcmp(ename, "sample") == 0)
+ _tos->prof.what = Profsample;
+ }
+ _tos->prof.first = sbrk(n*sizeof(Plink));
+ _tos->prof.last = sbrk(0);
+ _tos->prof.next = _tos->prof.first;
+ _tos->prof.pp = NULL;
+ _tos->prof.pid = _tos->pid;
+ atexit(_profdump);
+ _tos->clock = 1;
+}
+
+void prof(void (*fn)(void*), void *arg, int entries, int what)
+{
+ _profinit(entries, what);
+ _tos->prof.pp = _tos->prof.next;
+ fn(arg);
+ _profdump();
+}
+
+#pragma profile on
+
diff --git a/sys/src/ape/lib/ap/plan9/qlock.c b/sys/src/ape/lib/ap/plan9/qlock.c
new file mode 100755
index 000000000..b0882f789
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/qlock.c
@@ -0,0 +1,365 @@
+#define _LOCK_EXTENSION
+#define _QLOCK_EXTENSION
+#define _RESEARCH_SOURCE
+#include <u.h>
+#include <lock.h>
+#include <qlock.h>
+#include <stdlib.h>
+#include "sys9.h"
+
+#define rendezvous _RENDEZVOUS
+#define _rendezvousp rendezvous
+#define _tas tas
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+
+static struct {
+ QLp *p;
+ QLp x[1024];
+} ql = {
+ ql.x
+};
+
+enum
+{
+ Queuing,
+ QueuingR,
+ QueuingW,
+ Sleeping,
+};
+
+/* find a free shared memory location to queue ourselves in */
+static QLp*
+getqlp(void)
+{
+ QLp *p, *op;
+
+ op = ql.p;
+ for(p = op+1; ; p++){
+ if(p == &ql.x[nelem(ql.x)])
+ p = ql.x;
+ if(p == op)
+ abort();
+ if(_tas(&(p->inuse)) == 0){
+ ql.p = p;
+ p->next = nil;
+ break;
+ }
+ }
+ return p;
+}
+
+void
+qlock(QLock *q)
+{
+ QLp *p, *mp;
+
+ lock(&q->lock);
+ if(!q->locked){
+ q->locked = 1;
+ unlock(&q->lock);
+ return;
+ }
+
+
+ /* chain into waiting list */
+ mp = getqlp();
+ p = q->tail;
+ if(p == nil)
+ q->head = mp;
+ else
+ p->next = mp;
+ q->tail = mp;
+ mp->state = Queuing;
+ unlock(&q->lock);
+
+ /* wait */
+ while((*_rendezvousp)((ulong)mp, 1) == ~0)
+ ;
+ mp->inuse = 0;
+}
+
+void
+qunlock(QLock *q)
+{
+ QLp *p;
+
+ lock(&q->lock);
+ p = q->head;
+ if(p != nil){
+ /* wakeup head waiting process */
+ q->head = p->next;
+ if(q->head == nil)
+ q->tail = nil;
+ unlock(&q->lock);
+ while((*_rendezvousp)((ulong)p, 0x12345) == ~0)
+ ;
+ return;
+ }
+ q->locked = 0;
+ unlock(&q->lock);
+}
+
+int
+canqlock(QLock *q)
+{
+ if(!canlock(&q->lock))
+ return 0;
+ if(!q->locked){
+ q->locked = 1;
+ unlock(&q->lock);
+ return 1;
+ }
+ unlock(&q->lock);
+ return 0;
+}
+
+#if 0
+
+void
+rlock(RWLock *q)
+{
+ QLp *p, *mp;
+
+ lock(&q->lock);
+ if(q->writer == 0 && q->head == nil){
+ /* no writer, go for it */
+ q->readers++;
+ unlock(&q->lock);
+ return;
+ }
+
+ mp = getqlp();
+ p = q->tail;
+ if(p == 0)
+ q->head = mp;
+ else
+ p->next = mp;
+ q->tail = mp;
+ mp->next = nil;
+ mp->state = QueuingR;
+ unlock(&q->lock);
+
+ /* wait in kernel */
+ while((*_rendezvousp)((ulong)mp, 1) == ~0)
+ ;
+ mp->inuse = 0;
+}
+
+int
+canrlock(RWLock *q)
+{
+ lock(&q->lock);
+ if (q->writer == 0 && q->head == nil) {
+ /* no writer; go for it */
+ q->readers++;
+ unlock(&q->lock);
+ return 1;
+ }
+ unlock(&q->lock);
+ return 0;
+}
+
+void
+runlock(RWLock *q)
+{
+ QLp *p;
+
+ lock(&q->lock);
+ if(q->readers <= 0)
+ abort();
+ p = q->head;
+ if(--(q->readers) > 0 || p == nil){
+ unlock(&q->lock);
+ return;
+ }
+
+ /* start waiting writer */
+ if(p->state != QueuingW)
+ abort();
+ q->head = p->next;
+ if(q->head == 0)
+ q->tail = 0;
+ q->writer = 1;
+ unlock(&q->lock);
+
+ /* wakeup waiter */
+ while((*_rendezvousp)((ulong)p, 0) == ~0)
+ ;
+}
+
+void
+wlock(RWLock *q)
+{
+ QLp *p, *mp;
+
+ lock(&q->lock);
+ if(q->readers == 0 && q->writer == 0){
+ /* noone waiting, go for it */
+ q->writer = 1;
+ unlock(&q->lock);
+ return;
+ }
+
+ /* wait */
+ p = q->tail;
+ mp = getqlp();
+ if(p == nil)
+ q->head = mp;
+ else
+ p->next = mp;
+ q->tail = mp;
+ mp->next = nil;
+ mp->state = QueuingW;
+ unlock(&q->lock);
+
+ /* wait in kernel */
+ while((*_rendezvousp)((ulong)mp, 1) == ~0)
+ ;
+ mp->inuse = 0;
+}
+
+int
+canwlock(RWLock *q)
+{
+ lock(&q->lock);
+ if (q->readers == 0 && q->writer == 0) {
+ /* no one waiting; go for it */
+ q->writer = 1;
+ unlock(&q->lock);
+ return 1;
+ }
+ unlock(&q->lock);
+ return 0;
+}
+
+void
+wunlock(RWLock *q)
+{
+ QLp *p;
+
+ lock(&q->lock);
+ if(q->writer == 0)
+ abort();
+ p = q->head;
+ if(p == nil){
+ q->writer = 0;
+ unlock(&q->lock);
+ return;
+ }
+ if(p->state == QueuingW){
+ /* start waiting writer */
+ q->head = p->next;
+ if(q->head == nil)
+ q->tail = nil;
+ unlock(&q->lock);
+ while((*_rendezvousp)((ulong)p, 0) == ~0)
+ ;
+ return;
+ }
+
+ if(p->state != QueuingR)
+ abort();
+
+ /* wake waiting readers */
+ while(q->head != nil && q->head->state == QueuingR){
+ p = q->head;
+ q->head = p->next;
+ q->readers++;
+ while((*_rendezvousp)((ulong)p, 0) == ~0)
+ ;
+ }
+ if(q->head == nil)
+ q->tail = nil;
+ q->writer = 0;
+ unlock(&q->lock);
+}
+
+void
+rsleep(Rendez *r)
+{
+ QLp *t, *me;
+
+ if(!r->l)
+ abort();
+ lock(&r->l->lock);
+ /* we should hold the qlock */
+ if(!r->l->locked)
+ abort();
+
+ /* add ourselves to the wait list */
+ me = getqlp();
+ me->state = Sleeping;
+ if(r->head == nil)
+ r->head = me;
+ else
+ r->tail->next = me;
+ me->next = nil;
+ r->tail = me;
+
+ /* pass the qlock to the next guy */
+ t = r->l->head;
+ if(t){
+ r->l->head = t->next;
+ if(r->l->head == nil)
+ r->l->tail = nil;
+ unlock(&r->l->lock);
+ while((*_rendezvousp)((ulong)t, 0x12345) == ~0)
+ ;
+ }else{
+ r->l->locked = 0;
+ unlock(&r->l->lock);
+ }
+
+ /* wait for a wakeup */
+ while((*_rendezvousp)((ulong)me, 1) == ~0)
+ ;
+ me->inuse = 0;
+}
+
+int
+rwakeup(Rendez *r)
+{
+ QLp *t;
+
+ /*
+ * take off wait and put on front of queue
+ * put on front so guys that have been waiting will not get starved
+ */
+
+ if(!r->l)
+ abort();
+ lock(&r->l->lock);
+ if(!r->l->locked)
+ abort();
+
+ t = r->head;
+ if(t == nil){
+ unlock(&r->l->lock);
+ return 0;
+ }
+
+ r->head = t->next;
+ if(r->head == nil)
+ r->tail = nil;
+
+ t->next = r->l->head;
+ r->l->head = t;
+ if(r->l->tail == nil)
+ r->l->tail = t;
+
+ t->state = Queuing;
+ unlock(&r->l->lock);
+ return 1;
+}
+
+int
+rwakeupall(Rendez *r)
+{
+ int i;
+
+ for(i=0; rwakeup(r); i++)
+ ;
+ return i;
+}
+
+#endif
diff --git a/sys/src/ape/lib/ap/plan9/read.c b/sys/src/ape/lib/ap/plan9/read.c
new file mode 100755
index 000000000..471ab7e33
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/read.c
@@ -0,0 +1,46 @@
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include "lib.h"
+#include "sys9.h"
+
+#include <stdio.h>
+
+ssize_t
+read(int d, void *buf, size_t nbytes)
+{
+ int n, noblock, isbuf;
+ Fdinfo *f;
+
+ if(d<0 || d>=OPEN_MAX || !((f = &_fdinfo[d])->flags&FD_ISOPEN)){
+ errno = EBADF;
+ return -1;
+ }
+ if(nbytes <= 0)
+ return 0;
+ if(buf == 0){
+ errno = EFAULT;
+ return -1;
+ }
+ f = &_fdinfo[d];
+ noblock = f->oflags&O_NONBLOCK;
+ isbuf = f->flags&(FD_BUFFERED|FD_BUFFEREDX);
+ if(noblock || isbuf){
+ if(f->flags&FD_BUFFEREDX) {
+ errno = EIO;
+ return -1;
+ }
+ if(!isbuf) {
+ if(_startbuf(d) != 0) {
+ errno = EIO;
+ return -1;
+ }
+ }
+ n = _readbuf(d, buf, nbytes, noblock);
+ }else{
+ n = _READ(d, buf, nbytes);
+ if(n < 0)
+ _syserrno();
+ }
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/rename.c b/sys/src/ape/lib/ap/plan9/rename.c
new file mode 100755
index 000000000..dddfea328
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/rename.c
@@ -0,0 +1,76 @@
+#include "lib.h"
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include "dir.h"
+
+int
+rename(const char *from, const char *to)
+{
+ int n, i;
+ char *f, *t;
+ Dir *d, nd;
+ long mode;
+
+ if(access(to, 0) >= 0){
+ if(_REMOVE(to) < 0){
+ _syserrno();
+ return -1;
+ }
+ }
+ if((d = _dirstat(to)) != nil){
+ free(d);
+ errno = EEXIST;
+ return -1;
+ }
+ if((d = _dirstat(from)) == nil){
+ _syserrno();
+ return -1;
+ }
+ f = strrchr(from, '/');
+ t = strrchr(to, '/');
+ f = f? f+1 : from;
+ t = t? t+1 : to;
+ n = 0;
+ if(f-from==t-to && strncmp(from, to, f-from)==0){
+ /* from and to are in same directory (we miss some cases) */
+ i = strlen(t);
+ _nulldir(&nd);
+ nd.name = t;
+ if(_dirwstat(from, &nd) < 0){
+ _syserrno();
+ n = -1;
+ }
+ }else{
+ /* different directories: have to copy */
+ int ffd, tfd;
+ char buf[8192];
+
+ if((ffd = _OPEN(from, 0)) < 0 ||
+ (tfd = _CREATE(to, 1, d->mode)) < 0){
+ _CLOSE(ffd);
+ _syserrno();
+ n = -1;
+ }
+ while(n>=0 && (n = _READ(ffd, buf, 8192)) > 0)
+ if(_WRITE(tfd, buf, n) != n){
+ _syserrno();
+ n = -1;
+ }
+ _CLOSE(ffd);
+ _CLOSE(tfd);
+ if(n>0)
+ n = 0;
+ if(n == 0) {
+ if(_REMOVE(from) < 0){
+ _syserrno();
+ return -1;
+ }
+ }
+ }
+ free(d);
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/rmdir.c b/sys/src/ape/lib/ap/plan9/rmdir.c
new file mode 100755
index 000000000..a799bc00b
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/rmdir.c
@@ -0,0 +1,17 @@
+#include "lib.h"
+#include <errno.h>
+#include <unistd.h>
+#include "sys9.h"
+
+int
+rmdir(const char *path)
+{
+ int n;
+
+ n = 0;
+ if(_REMOVE(path) < 0) {
+ _syserrno();
+ n = -1;
+ }
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/setgid.c b/sys/src/ape/lib/ap/plan9/setgid.c
new file mode 100755
index 000000000..7c9075c3f
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/setgid.c
@@ -0,0 +1,14 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+/*
+ * BUG: never works
+ */
+
+int
+setgid(gid_t gid)
+{
+ errno = EPERM;
+ return -1;
+}
diff --git a/sys/src/ape/lib/ap/plan9/setpgid.c b/sys/src/ape/lib/ap/plan9/setpgid.c
new file mode 100755
index 000000000..dd5445085
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/setpgid.c
@@ -0,0 +1,31 @@
+#include "lib.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include "sys9.h"
+
+int
+setpgid(pid_t pid, pid_t pgid)
+{
+ int n, f;
+ char buf[50], fname[30];
+
+ if(pid == 0)
+ pid = getpid();
+ if(pgid == 0)
+ pgid = getpgrp();
+ sprintf(fname, "/proc/%d/noteid", pid);
+ f = open(fname, 1);
+ if(f < 0) {
+ errno = ESRCH;
+ return -1;
+ }
+ n = sprintf(buf, "%d", pgid);
+ n = write(f, buf, n);
+ if(n < 0)
+ _syserrno();
+ else
+ n = 0;
+ close(f);
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/setsid.c b/sys/src/ape/lib/ap/plan9/setsid.c
new file mode 100755
index 000000000..f53f8f2f3
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/setsid.c
@@ -0,0 +1,14 @@
+#include "lib.h"
+#include <unistd.h>
+#include "sys9.h"
+
+pid_t
+setsid(void)
+{
+ if(_RFORK(RFNAMEG|RFNOTEG) < 0){
+ _syserrno();
+ return -1;
+ }
+ _sessleader = 1;
+ return getpgrp();
+}
diff --git a/sys/src/ape/lib/ap/plan9/setuid.c b/sys/src/ape/lib/ap/plan9/setuid.c
new file mode 100755
index 000000000..a34f2de6f
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/setuid.c
@@ -0,0 +1,14 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+/*
+ * BUG: never works
+ */
+
+int
+setuid(uid_t uid)
+{
+ errno = EPERM;
+ return -1;
+}
diff --git a/sys/src/ape/lib/ap/plan9/signal.c b/sys/src/ape/lib/ap/plan9/signal.c
new file mode 100755
index 000000000..cf8a823ef
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/signal.c
@@ -0,0 +1,136 @@
+#include "lib.h"
+#include "sys9.h"
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <setjmp.h>
+
+extern sigset_t _psigblocked;
+
+static struct {
+ char *msg; /* just check prefix */
+ int num;
+} sigtab[] = {
+ {"hangup", SIGHUP},
+ {"interrupt", SIGINT},
+ {"quit", SIGQUIT},
+ {"alarm", SIGALRM},
+ {"sys: trap: illegal instruction", SIGILL},
+ {"sys: trap: reserved instruction", SIGILL},
+ {"sys: trap: reserved", SIGILL},
+ {"sys: trap: arithmetic overflow", SIGFPE},
+ {"abort", SIGABRT},
+ {"sys: fp:", SIGFPE},
+ {"exit", SIGKILL},
+ {"die", SIGKILL},
+ {"kill", SIGKILL},
+ {"sys: trap: bus error", SIGSEGV},
+ {"sys: trap: address error", SIGSEGV},
+ {"sys: trap: TLB", SIGSEGV},
+ {"sys: write on closed pipe", SIGPIPE},
+ {"alarm", SIGALRM},
+ {"term", SIGTERM},
+ {"usr1", SIGUSR1},
+ {"usr2", SIGUSR2},
+};
+#define NSIGTAB ((sizeof sigtab)/(sizeof (sigtab[0])))
+
+void (*_sighdlr[MAXSIG+1])(int, char*, Ureg*); /* 0 initialized: SIG_DFL */
+
+void
+(*signal(int sig, void (*func)(int, char*, Ureg*)))(int, char*, Ureg*)
+{
+ void(*oldf)(int, char*, Ureg*);
+
+ if(sig <= 0 || sig > MAXSIG){
+ errno = EINVAL;
+ return SIG_ERR;
+ }
+ oldf = _sighdlr[sig];
+ if(sig == SIGKILL)
+ return oldf; /* can't catch or ignore SIGKILL */
+ _sighdlr[sig] = func;
+ return oldf;
+}
+
+/* BAD CODE - see /sys/src/ape/lib/ap/$objtype/setjmp.s for real code
+int
+sigsetjmp(sigjmp_buf buf, int savemask)
+{
+ int r;
+
+ buf[0] = savemask;
+ buf[1] = _psigblocked;
+ return setjmp(&buf[2]);
+}
+*/
+
+/*
+ * BUG: improper handling of process signal mask
+ */
+int
+sigaction(int sig, struct sigaction *act, struct sigaction *oact)
+{
+ if(sig <= 0 || sig > MAXSIG || sig == SIGKILL){
+ errno = EINVAL;
+ return -1;
+ }
+ if(oact){
+ oact->sa_handler = _sighdlr[sig];
+ oact->sa_mask = _psigblocked;
+ oact->sa_flags = 0;
+ }
+ if(act){
+ _sighdlr[sig] = act->sa_handler;
+ }
+ return 0;
+}
+
+/* this is registered in _envsetup */
+int
+_notehandler(Ureg *u, char *msg)
+{
+ int i;
+ void(*f)(int, char*, Ureg*);
+ extern void _doatexits(void); /* in stdio/exit.c */
+
+ if(_finishing)
+ _finish(0, 0);
+ for(i = 0; i<NSIGTAB; i++){
+ if(strncmp(msg, sigtab[i].msg, strlen(sigtab[i].msg)) == 0){
+ f = _sighdlr[sigtab[i].num];
+ if(f == SIG_DFL || f == SIG_ERR)
+ break;
+ if(f != SIG_IGN) {
+ _notetramp(sigtab[i].num, f, u);
+ /* notetramp is machine-dependent; doesn't return to here */
+ }
+ _NOTED(0); /* NCONT */
+ return;
+ }
+ }
+ _doatexits();
+ _NOTED(1); /* NDFLT */
+}
+
+int
+_stringsig(char *nam)
+{
+ int i;
+
+ for(i = 0; i<NSIGTAB; i++)
+ if(strncmp(nam, sigtab[i].msg, strlen(sigtab[i].msg)) == 0)
+ return sigtab[i].num;
+ return 0;
+}
+
+char *
+_sigstring(int sig)
+{
+ int i;
+
+ for(i=0; i<NSIGTAB; i++)
+ if(sigtab[i].num == sig)
+ return sigtab[i].msg;
+ return "unknown signal";
+}
diff --git a/sys/src/ape/lib/ap/plan9/sigpending.c b/sys/src/ape/lib/ap/plan9/sigpending.c
new file mode 100755
index 000000000..8b5eb2eda
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/sigpending.c
@@ -0,0 +1,11 @@
+#include <signal.h>
+
+/*
+ * BUG: don't keep track of these
+ */
+int
+sigpending(sigset_t *set)
+{
+ *set = 0;
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/sigprocmask.c b/sys/src/ape/lib/ap/plan9/sigprocmask.c
new file mode 100755
index 000000000..84b94f987
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/sigprocmask.c
@@ -0,0 +1,23 @@
+#include "lib.h"
+#include <signal.h>
+#include <errno.h>
+
+sigset_t _psigblocked;
+
+int
+sigprocmask(int how, sigset_t *set, sigset_t *oset)
+{
+ if(oset)
+ *oset = _psigblocked;
+ if(how==SIG_BLOCK)
+ _psigblocked |= *set;
+ else if(how==SIG_UNBLOCK)
+ _psigblocked &= ~*set;
+ else if(how==SIG_SETMASK)
+ _psigblocked = *set;
+ else{
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/sigsuspend.c b/sys/src/ape/lib/ap/plan9/sigsuspend.c
new file mode 100755
index 000000000..290c15d0f
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/sigsuspend.c
@@ -0,0 +1,13 @@
+#include <signal.h>
+#include <errno.h>
+
+/*
+ * BUG: doesn't work
+ */
+
+int
+sigsuspend(sigset_t *set)
+{
+ errno = EINVAL;
+ return -1;
+}
diff --git a/sys/src/ape/lib/ap/plan9/sleep.c b/sys/src/ape/lib/ap/plan9/sleep.c
new file mode 100755
index 000000000..a6fcf19a9
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/sleep.c
@@ -0,0 +1,17 @@
+#include "lib.h"
+#include <unistd.h>
+#include <time.h>
+#include "sys9.h"
+
+unsigned int
+sleep(unsigned int secs)
+{
+ time_t t0, t1;
+
+ t0 = time(0);
+ if(_SLEEP(secs*1000) < 0){
+ t1 = time(0);
+ return t1-t0;
+ }
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/sqrt.c b/sys/src/ape/lib/ap/plan9/sqrt.c
new file mode 100755
index 000000000..98974360d
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/sqrt.c
@@ -0,0 +1,56 @@
+/*
+ sqrt returns the square root of its floating
+ point argument. Newton's method.
+
+ calls frexp
+*/
+
+#include <math.h>
+#include <errno.h>
+#define _RESEARCH_SOURCE
+#include <float.h>
+
+double
+sqrt(double arg)
+{
+ double x, temp;
+ int exp, i;
+
+ if(isInf(arg, 1))
+ return arg;
+ if(arg <= 0) {
+ if(arg < 0)
+ errno = EDOM;
+ return 0;
+ }
+ x = frexp(arg, &exp);
+ while(x < 0.5) {
+ x *= 2;
+ exp--;
+ }
+ /*
+ * NOTE
+ * this wont work on 1's comp
+ */
+ if(exp & 1) {
+ x *= 2;
+ exp--;
+ }
+ temp = 0.5 * (1.0+x);
+
+ while(exp > 60) {
+ temp *= (1L<<30);
+ exp -= 60;
+ }
+ while(exp < -60) {
+ temp /= (1L<<30);
+ exp += 60;
+ }
+ if(exp >= 0)
+ temp *= 1L << (exp/2);
+ else
+ temp /= 1L << (-exp/2);
+ for(i=0; i<=4; i++)
+ temp = 0.5*(temp + arg/temp);
+ return temp;
+}
diff --git a/sys/src/ape/lib/ap/plan9/stat.c b/sys/src/ape/lib/ap/plan9/stat.c
new file mode 100755
index 000000000..be0d7c484
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/stat.c
@@ -0,0 +1,21 @@
+#include "lib.h"
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include "dir.h"
+
+int
+stat(const char *path, struct stat *buf)
+{
+ Dir *d;
+
+ if((d = _dirstat(path)) == nil){
+ _syserrno();
+ return -1;
+ }
+ _dirtostat(buf, d, 0);
+ free(d);
+
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/sys9.h b/sys/src/ape/lib/ap/plan9/sys9.h
new file mode 100755
index 000000000..b289273cb
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/sys9.h
@@ -0,0 +1,121 @@
+typedef
+struct Waitmsg
+{
+ int pid; /* of loved one */
+ unsigned long time[3]; /* of loved one & descendants */
+ char *msg;
+} Waitmsg;
+
+#define STATMAX 65535U /* max length of machine-independent stat structure */
+#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */
+#define ERRMAX 128 /* max length of error string */
+
+#define MORDER 0x0003 /* mask for bits defining order of mounting */
+#define MREPL 0x0000 /* mount replaces object */
+#define MBEFORE 0x0001 /* mount goes before others in union directory */
+#define MAFTER 0x0002 /* mount goes after others in union directory */
+#define MCREATE 0x0004 /* permit creation in mounted directory */
+#define MCACHE 0x0010 /* cache some data */
+#define MMASK 0x0007 /* all bits on */
+
+#define OREAD 0 /* open for read */
+#define OWRITE 1 /* write */
+#define ORDWR 2 /* read and write */
+#define OEXEC 3 /* execute, == read but check execute permission */
+#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */
+#define OCEXEC 32 /* or'ed in, close on exec */
+#define ORCLOSE 64 /* or'ed in, remove on close */
+#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */
+
+#define AEXIST 0 /* accessible: exists */
+#define AEXEC 1 /* execute access */
+#define AWRITE 2 /* write access */
+#define AREAD 4 /* read access */
+
+/* Segattch */
+#define SG_RONLY 0040 /* read only */
+#define SG_CEXEC 0100 /* detach on exec */
+
+#define NCONT 0 /* continue after note */
+#define NDFLT 1 /* terminate after note */
+#define NSAVE 2 /* clear note but hold state */
+#define NRSTR 3 /* restore saved state */
+
+/* bits in Qid.type */
+#define QTDIR 0x80 /* type bit for directories */
+#define QTAPPEND 0x40 /* type bit for append only files */
+#define QTEXCL 0x20 /* type bit for exclusive use files */
+#define QTMOUNT 0x10 /* type bit for mounted channel */
+#define QTFILE 0x00 /* plain file */
+
+/* bits in Dir.mode */
+#define DMDIR 0x80000000 /* mode bit for directories */
+#define DMAPPEND 0x40000000 /* mode bit for append only files */
+#define DMEXCL 0x20000000 /* mode bit for exclusive use files */
+#define DMMOUNT 0x10000000 /* mode bit for mounted channel */
+#define DMREAD 0x4 /* mode bit for read permission */
+#define DMWRITE 0x2 /* mode bit for write permission */
+#define DMEXEC 0x1 /* mode bit for execute permission */
+
+/* rfork */
+enum
+{
+ RFNAMEG = (1<<0),
+ RFENVG = (1<<1),
+ RFFDG = (1<<2),
+ RFNOTEG = (1<<3),
+ RFPROC = (1<<4),
+ RFMEM = (1<<5),
+ RFNOWAIT = (1<<6),
+ RFCNAMEG = (1<<10),
+ RFCENVG = (1<<11),
+ RFCFDG = (1<<12),
+ RFREND = (1<<13),
+ RFNOMNT = (1<<14)
+};
+
+extern int _AWAIT(char*, int);
+extern int _ALARM(unsigned long);
+extern int _BIND(const char*, const char*, int);
+extern int _CHDIR(const char*);
+extern int _CLOSE(int);
+extern int _CREATE(char*, int, unsigned long);
+extern int _DUP(int, int);
+extern int _ERRSTR(char*, unsigned int);
+extern int _EXEC(char*, char*[]);
+extern void _EXITS(char *);
+extern int _FD2PATH(int, char*, int);
+extern int _FAUTH(int, char*);
+extern int _FSESSION(int, char*, int);
+extern int _FSTAT(int, unsigned char*, int);
+extern int _FWSTAT(int, unsigned char*, int);
+extern int _MOUNT(int, int, const char*, int, const char*);
+extern int _NOTED(int);
+extern int _NOTIFY(int(*)(void*, char*));
+extern int _OPEN(const char*, int);
+extern int _PIPE(int*);
+extern long _PREAD(int, void*, long, long long);
+extern long _PWRITE(int, void*, long, long long);
+extern long _READ(int, void*, long);
+extern int _REMOVE(const char*);
+extern int _RENDEZVOUS(unsigned long, unsigned long);
+extern int _RFORK(int);
+extern int _SEGATTACH(int, char*, void*, unsigned long);
+extern int _SEGBRK(void*, void*);
+extern int _SEGDETACH(void*);
+extern int _SEGFLUSH(void*, unsigned long);
+extern int _SEGFREE(void*, unsigned long);
+extern long long _SEEK(int, long long, int);
+extern int _SLEEP(long);
+extern int _STAT(const char*, unsigned char*, int);
+extern Waitmsg* _WAIT(void);
+extern long _WRITE(int, const void*, long);
+extern int _WSTAT(const char*, unsigned char*, int);
+
+extern int __open(char *, int, ...);
+extern int __access(char *, int);
+extern int __chdir(char *);
+extern int __creat(char *, int);
+extern int __link(char *, int);
+extern int __stat(char *, struct stat *);
+extern int __unlink(char *);
diff --git a/sys/src/ape/lib/ap/plan9/system.c b/sys/src/ape/lib/ap/plan9/system.c
new file mode 100755
index 000000000..497b0e1f1
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/system.c
@@ -0,0 +1,40 @@
+#include "lib.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+int
+system(const char *s)
+{
+ int w, status;
+ pid_t pid;
+ char cmd[30], *oty;
+
+ oty = getenv("cputype");
+ if(!oty)
+ return -1;
+ if(!s)
+ return 1; /* a command interpreter is available */
+ pid = fork();
+ snprintf(cmd, sizeof cmd, "/%s/bin/ape/sh", oty);
+ if(pid == 0) {
+ execl(cmd, "sh", "-c", s, NULL);
+ _exit(1);
+ }
+ if(pid < 0){
+ _syserrno();
+ return -1;
+ }
+ for(;;) {
+ w = wait(&status);
+ if(w == -1 || w == pid)
+ break;
+ }
+
+ if(w == -1){
+ _syserrno();
+ return w;
+ }
+ return status;
+}
diff --git a/sys/src/ape/lib/ap/plan9/tcgetattr.c b/sys/src/ape/lib/ap/plan9/tcgetattr.c
new file mode 100755
index 000000000..8a7d3e608
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/tcgetattr.c
@@ -0,0 +1,201 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "sys9.h"
+#include "lib.h"
+#include "dir.h"
+
+#define CESC '\\'
+#define CINTR 0177 /* DEL */
+#define CQUIT 034 /* FS, cntl | */
+#define CERASE 010 /* BS */
+#define CKILL 025 /* cntl u */
+#define CEOF 04 /* cntl d */
+#define CSTART 021 /* cntl q */
+#define CSTOP 023 /* cntl s */
+#define CSWTCH 032 /* cntl z */
+#define CEOL 000
+#define CNSWTCH 0
+
+static int
+isptty(int fd)
+{
+ Dir *d;
+ int rv;
+
+ if((d = _dirfstat(fd)) == nil)
+ return 0;
+ rv = (strncmp(d->name, "ptty", 4) == 0);
+ free(d);
+ return rv;
+}
+
+int
+tcgetattr(int fd, struct termios *t)
+{
+ int n;
+ char buf[60];
+
+ if(!isptty(fd)) {
+ if(isatty(fd)) {
+ /* If there is no emulation return sensible defaults */
+ t->c_iflag = ISTRIP|ICRNL|IXON|IXOFF;
+ t->c_oflag = OPOST|TAB3|ONLCR;
+ t->c_cflag = B9600;
+ t->c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
+ t->c_cc[VINTR] = CINTR;
+ t->c_cc[VQUIT] = CQUIT;
+ t->c_cc[VERASE] = CERASE;
+ t->c_cc[VKILL] = CKILL;
+ t->c_cc[VEOF] = CEOF;
+ t->c_cc[VEOL] = CEOL;
+ t->c_cc[VSTART] = CSTART;
+ t->c_cc[VSTOP] = CSTOP;
+ return 0;
+ } else {
+ errno = ENOTTY;
+ return -1;
+ }
+ }
+ if(_SEEK(fd, -2, 0) != -2) {
+ _syserrno();
+ return -1;
+ }
+
+ n = _READ(fd, buf, 57);
+ if(n < 0) {
+ _syserrno();
+ return -1;
+ }
+
+ t->c_iflag = strtoul(buf+4, 0, 16);
+ t->c_oflag = strtoul(buf+9, 0, 16);
+ t->c_cflag = strtoul(buf+14, 0, 16);
+ t->c_lflag = strtoul(buf+19, 0, 16);
+
+ for(n = 0; n < NCCS; n++)
+ t->c_cc[n] = strtoul(buf+24+(n*3), 0, 16);
+
+ return 0;
+}
+
+/* BUG: ignores optional actions */
+
+int
+tcsetattr(int fd, int optactions, const struct termios *t)
+{
+ int n, i;
+ char buf[100];
+
+ if(!isptty(fd)) {
+ if(!isatty(fd)) {
+ errno = ENOTTY;
+ return -1;
+ } else
+ return 0;
+ }
+ n = sprintf(buf, "IOW %4.4x %4.4x %4.4x %4.4x ",
+ t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag);
+
+ for(i = 0; i < NCCS; i++)
+ n += sprintf(buf+n, "%2.2x ", t->c_cc[i]);
+
+ if(_SEEK(fd, -2, 0) != -2) {
+ _syserrno();
+ return -1;
+ }
+
+ n = _WRITE(fd, buf, n);
+ if(n < 0) {
+ _syserrno();
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+tcsetpgrp(int fd, pid_t pgrpid)
+{
+ int n;
+ char buf[30];
+
+ if(!isptty(fd)) {
+ if(!isatty(fd)) {
+ errno = ENOTTY;
+ return -1;
+ } else
+ return 0;
+ }
+ n = sprintf(buf, "IOW note %d", pgrpid);
+
+ if(_SEEK(fd, -2, 0) != -2) {
+ _syserrno();
+ return -1;
+ }
+
+ n = _WRITE(fd, buf, n);
+ if(n < 0) {
+ _syserrno();
+ return -1;
+ }
+}
+
+pid_t
+tcgetpgrp(int fd)
+{
+ int n;
+ pid_t pgrp;
+ char buf[100];
+
+ if(!isptty(fd)) {
+ errno = ENOTTY;
+ return -1;
+ }
+ if(_SEEK(fd, -2, 0) != -2) {
+ _syserrno();
+ return -1;
+ }
+ n = _READ(fd, buf, sizeof(buf));
+ if(n < 0) {
+ _syserrno();
+ return -1;
+ }
+ pgrp = atoi(buf+24+(NCCS*3));
+ return pgrp;
+}
+
+/* should do a better job here */
+
+int
+tcdrain(int)
+{
+ errno = ENOTTY;
+ return -1;
+}
+
+int
+tcflush(int, int)
+{
+ errno = ENOTTY;
+ return -1;
+}
+
+int
+tcflow(int, int)
+{
+ errno = ENOTTY;
+ return -1;
+}
+
+int
+tcsendbreak(int)
+{
+ errno = ENOTTY;
+ return -1;
+}
diff --git a/sys/src/ape/lib/ap/plan9/time.c b/sys/src/ape/lib/ap/plan9/time.c
new file mode 100755
index 000000000..85a1e72b2
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/time.c
@@ -0,0 +1,27 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include "sys9.h"
+
+time_t
+time(time_t *tp)
+{
+ char b[20];
+ int f;
+ time_t t;
+
+ memset(b, 0, sizeof(b));
+ f = _OPEN("/dev/time", 0);
+ if(f >= 0) {
+ _PREAD(f, b, sizeof(b), 0);
+ _CLOSE(f);
+ }
+ t = atol(b);
+ if(tp)
+ *tp = t;
+ return t;
+}
diff --git a/sys/src/ape/lib/ap/plan9/times.c b/sys/src/ape/lib/ap/plan9/times.c
new file mode 100755
index 000000000..2d1e95c8c
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/times.c
@@ -0,0 +1,51 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/times.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+static
+char*
+skip(char *p)
+{
+
+ while(*p == ' ')
+ p++;
+ while(*p != ' ' && *p != 0)
+ p++;
+ return p;
+}
+
+clock_t
+times(struct tms *buf)
+{
+ char b[200], *p;
+ int f;
+ unsigned long r;
+
+ memset(b, 0, sizeof(b));
+ f = open("/dev/cputime", O_RDONLY);
+ if(f >= 0) {
+ lseek(f, SEEK_SET, 0);
+ read(f, b, sizeof(b));
+ close(f);
+ }
+ p = b;
+ if(buf)
+ buf->tms_utime = atol(p);
+ p = skip(p);
+ if(buf)
+ buf->tms_stime = atol(p);
+ p = skip(p);
+ r = atol(p);
+ if(buf){
+ p = skip(p);
+ buf->tms_cutime = atol(p);
+ p = skip(p);
+ buf->tms_cstime = atol(p);
+ }
+ return r;
+}
diff --git a/sys/src/ape/lib/ap/plan9/tmpfile.c b/sys/src/ape/lib/ap/plan9/tmpfile.c
new file mode 100755
index 000000000..c3ab5fd88
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/tmpfile.c
@@ -0,0 +1,44 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "sys9.h"
+#undef OPEN
+#include "../stdio/iolib.h"
+#include "lib.h"
+#include "dir.h"
+
+FILE *
+tmpfile(void){
+ FILE *f;
+ static char name[]="/tmp/tf0000000000000";
+ char *p;
+ int n;
+ for(f=_IO_stream;f!=&_IO_stream[FOPEN_MAX];f++)
+ if(f->state==CLOSED)
+ break;
+ if(f==&_IO_stream[FOPEN_MAX])
+ return NULL;
+ while(access(name, 0) >= 0){
+ p = name+7;
+ while(*p == '9')
+ *p++ = '0';
+ if(*p == '\0')
+ return NULL;
+ ++*p;
+ }
+ n = _CREATE(name, 64|2, 0777); /* remove-on-close */
+ if(n==-1){
+ _syserrno();
+ return NULL;
+ }
+ _fdinfo[n].flags = FD_ISOPEN;
+ _fdinfo[n].oflags = 2;
+ f->fd=n;
+ f->flags=0;
+ f->state=OPEN;
+ f->buf=0;
+ f->rp=0;
+ f->wp=0;
+ f->lp=0;
+ return f;
+}
diff --git a/sys/src/ape/lib/ap/plan9/ttyname.c b/sys/src/ape/lib/ap/plan9/ttyname.c
new file mode 100755
index 000000000..980186a81
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/ttyname.c
@@ -0,0 +1,11 @@
+#include <unistd.h>
+#include <stdio.h>
+
+char *
+ttyname(int fd)
+{
+ static char buf[100];
+
+ sprintf(buf, "/fd/%d", fd);
+ return buf;
+}
diff --git a/sys/src/ape/lib/ap/plan9/umask.c b/sys/src/ape/lib/ap/plan9/umask.c
new file mode 100755
index 000000000..b45b7576c
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/umask.c
@@ -0,0 +1,13 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+/*
+ * No such concept in plan9, but supposed to be always successful
+ */
+
+mode_t
+umask(mode_t numask)
+{
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/uname.c b/sys/src/ape/lib/ap/plan9/uname.c
new file mode 100755
index 000000000..1df2a941f
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/uname.c
@@ -0,0 +1,25 @@
+#include <stdlib.h>
+#include <string.h>
+#include <sys/utsname.h>
+
+int
+uname(struct utsname *n)
+{
+ n->sysname = getenv("osname");
+ if(!n->sysname)
+ n->sysname = "Plan9";
+ n->nodename = getenv("sysname");
+ if(!n->nodename){
+ n->nodename = getenv("site");
+ if(!n->nodename)
+ n->nodename = "?";
+ }
+ n->release = "4"; /* edition */
+ n->version = "0";
+ n->machine = getenv("cputype");
+ if(!n->machine)
+ n->machine = "?";
+ if(strcmp(n->machine, "386") == 0)
+ n->machine = "i386"; /* for gnu configure */
+ return 0;
+}
diff --git a/sys/src/ape/lib/ap/plan9/unlink.c b/sys/src/ape/lib/ap/plan9/unlink.c
new file mode 100755
index 000000000..a015c782f
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/unlink.c
@@ -0,0 +1,75 @@
+#include "lib.h"
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include "dir.h"
+
+
+/*
+ * BUG: errno mapping
+ */
+
+int
+unlink(const char *path)
+{
+ int n, i, fd;
+ long long nn;
+ Dir *db1, *db2, nd;
+ Fdinfo *f;
+ char *p, newname[PATH_MAX], newelem[32];
+
+ /* if the file is already open, make it close-on-exec (and rename to qid) */
+ if((db1 = _dirstat(path)) == nil) {
+ _syserrno();
+ return -1;
+ }
+ fd = -1;
+ for(i=0, f = _fdinfo;i < OPEN_MAX; i++, f++) {
+ if((f->flags&FD_ISOPEN) && (db2=_dirfstat(i)) != nil) {
+ if(db1->qid.path == db2->qid.path &&
+ db1->qid.vers == db2->qid.vers &&
+ db1->type == db2->type &&
+ db1->dev == db2->dev) {
+ sprintf(newelem, "%8.8lx%8.8lx", (ulong)(db2->qid.path>>32), (ulong)db2->qid.path);
+ _nulldir(&nd);
+ nd.name = newelem;
+ if(_dirfwstat(i, &nd) < 0)
+ p = (char*)path;
+ else {
+ p = strrchr(path, '/');
+ if(p == 0)
+ p = newelem;
+ else {
+ memmove(newname, path, p-path);
+ newname[p-path] = '/';
+ strcpy(newname+(p-path)+1, newelem);
+ p = newname;
+ }
+ }
+ /* reopen remove on close */
+ fd = _OPEN(p, 64|(f->oflags));
+ if(fd < 0){
+ free(db2);
+ continue;
+ }
+ nn = _SEEK(i, 0, 1);
+ if(nn < 0)
+ nn = 0;
+ _SEEK(fd, nn, 0);
+ _DUP(fd, i);
+ _CLOSE(fd);
+ free(db1);
+ return 0;
+ }
+ free(db2);
+ }
+ }
+ if(fd == -1)
+ if((n=_REMOVE(path)) < 0)
+ _syserrno();
+ free(db1);
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/utime.c b/sys/src/ape/lib/ap/plan9/utime.c
new file mode 100755
index 000000000..52eeb34e2
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/utime.c
@@ -0,0 +1,30 @@
+#include "lib.h"
+#include <sys/types.h>
+#include <time.h>
+#include <utime.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "sys9.h"
+#include "dir.h"
+
+int
+utime(const char *path, const struct utimbuf *times)
+{
+ int n;
+ Dir nd;
+ time_t curt;
+
+ _nulldir(&nd);
+ if(times == 0) {
+ curt = time(0);
+ nd.atime = curt;
+ nd.mtime = curt;
+ } else {
+ nd.atime = times->actime;
+ nd.mtime = times->modtime;
+ }
+ n = _dirwstat(path, &nd);
+ if(n < 0)
+ _syserrno();
+ return n;
+}
diff --git a/sys/src/ape/lib/ap/plan9/wait.c b/sys/src/ape/lib/ap/plan9/wait.c
new file mode 100755
index 000000000..ac1ab13da
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/wait.c
@@ -0,0 +1,150 @@
+#include "lib.h"
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include "sys9.h"
+#include "dir.h"
+
+/*
+ * status not yet collected for processes that have exited
+ */
+typedef struct Waited Waited;
+struct Waited {
+ Waitmsg* msg;
+ Waited* next;
+};
+static Waited *wd;
+
+static Waitmsg *
+lookpid(int pid)
+{
+ Waited **wl, *w;
+ Waitmsg *msg;
+
+ for(wl = &wd; (w = *wl) != nil; wl = &w->next)
+ if(pid <= 0 || w->msg->pid == pid){
+ msg = w->msg;
+ *wl = w->next;
+ free(w);
+ return msg;
+ }
+ return 0;
+}
+
+static void
+addpid(Waitmsg *msg)
+{
+ Waited *w;
+
+ w = malloc(sizeof(*w));
+ if(w == nil){
+ /* lost it; what can we do? */
+ free(msg);
+ return;
+ }
+ w->msg = msg;
+ w->next = wd;
+ wd = w;
+}
+
+static int
+waitstatus(Waitmsg *w)
+{
+ int r, t;
+ char *bp, *ep;
+
+ r = 0;
+ t = 0;
+ if(w->msg[0]){
+ /* message is 'prog pid:string' */
+ bp = w->msg;
+ while(*bp){
+ if(*bp++ == ':')
+ break;
+ }
+ if(*bp == 0)
+ bp = w->msg;
+ r = strtol(bp, &ep, 10);
+ if(*ep == 0){
+ if(r < 0 || r >= 256)
+ r = 1;
+ }else{
+ t = _stringsig(bp);
+ if(t == 0)
+ r = 1;
+ }
+ }
+ return (r<<8) | t;
+}
+
+static void
+waitresource(struct rusage *ru, Waitmsg *w)
+{
+ memset(ru, 0, sizeof(*ru));
+ ru->ru_utime.tv_sec = w->time[0]/1000;
+ ru->ru_utime.tv_usec = (w->time[0]%1000)*1000;
+ ru->ru_stime.tv_sec = w->time[1]/1000;
+ ru->ru_stime.tv_usec = (w->time[1]%1000)*1000;
+}
+
+pid_t
+wait(int *status)
+{
+ return wait4(-1, status, 0, nil);
+}
+
+pid_t
+waitpid(pid_t wpid, int *status, int options)
+{
+ return wait4(wpid, status, options, nil);
+}
+
+pid_t
+wait3(int *status, int options, struct rusage *res)
+{
+ return wait4(-1, status, options, res);
+}
+
+pid_t
+wait4(pid_t wpid, int *status, int options, struct rusage *res)
+{
+ char pname[50];
+ Dir *d;
+ Waitmsg *w;
+
+ w = lookpid(wpid);
+ if(w == nil){
+ if(options & WNOHANG){
+ snprintf(pname, sizeof(pname), "/proc/%d/wait", getpid());
+ d = _dirstat(pname);
+ if(d != nil && d->length == 0){
+ free(d);
+ return 0;
+ }
+ free(d);
+ }
+ for(;;){
+ w = _WAIT();
+ if(w == nil){
+ _syserrno();
+ return -1;
+ }
+ if(wpid <= 0 || w->pid == wpid)
+ break;
+ addpid(w);
+ }
+ }
+ if(res != nil)
+ waitresource(res, w);
+ if(status != nil)
+ *status = waitstatus(w);
+ wpid = w->pid;
+ free(w);
+ return wpid;
+}
diff --git a/sys/src/ape/lib/ap/plan9/write.c b/sys/src/ape/lib/ap/plan9/write.c
new file mode 100755
index 000000000..6795bbc92
--- /dev/null
+++ b/sys/src/ape/lib/ap/plan9/write.c
@@ -0,0 +1,21 @@
+#include <errno.h>
+#include <unistd.h>
+#include "lib.h"
+#include "sys9.h"
+
+ssize_t
+write(int d, const void *buf, size_t nbytes)
+{
+ int n;
+
+ if(d<0 || d>=OPEN_MAX || !(_fdinfo[d].flags&FD_ISOPEN)){
+ errno = EBADF;
+ return -1;
+ }
+ if(_fdinfo[d].oflags&O_APPEND)
+ _SEEK(d, 0, 2);
+ n = _WRITE(d, buf, nbytes);
+ if(n < 0)
+ _syserrno();
+ return n;
+}