ref: 147d51bf7c6cd8798ddb26e33d857be13a66c548
parent: ef6ce918c04fc82bea759fd843cc09e273ec9f6c
author: henesy <devnull@localhost>
date: Sun Nov 4 12:33:14 EST 2018
add utils and such
--- /dev/null
+++ b/emu/DragonFly/README
@@ -1,0 +1,1 @@
+DragonFly BSD changes contributed by fgudin and extrudedaluminiu (see issue 181 at inferno-os.googlecode.com)
--- /dev/null
+++ b/emu/DragonFly/asm-386.S
@@ -1,0 +1,107 @@
+ .file "asm-DragonFly-386.S"
+#include <sys/syscall.h>
+
+/*
+ * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg)
+ */
+
+ .type ournewstack,@function
+ .global executeonnewstack
+executeonnewstack:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+
+ movl 8(%ebp), %esi /* get tos */
+ subl $4, %esi
+ movl 16(%ebp), %eax
+ movl %eax, (%esi) /* stash arg on new stack */
+ subl $4, %esi
+ movl 12(%ebp), %eax
+ movl %eax, (%esi) /* stash tramp on new stack */
+ mov %esi, %esp /* swap stacks pronto */
+ popl %eax /* recover the tramp address */
+ call *%eax /* and jump to it (ho ho) */
+
+ /* if we return here, tramp didn't do it's job */
+
+ addl $8, %esp /* clean up for pose value */
+
+ leal SYS_exit, %eax
+ int $0x80
+
+/*
+ * unlockandexit(int *key)
+ *
+ * NB: the return status may be rubbish if the stack is reused
+ * between the unlock and the system call, but this should
+ * not matter since no task is waiting for the result
+ */
+
+ .type unlockandexit,@function
+ .global unlockandexit
+unlockandexit:
+ pushl %ebp
+ movl %esp, %ebp
+
+ movl 8(%ebp), %esi /* get the key address */
+ pushl $0 /* exit status 0 */
+ movl $0, %eax /* unlock the stack allocator */
+ movl %eax, (%esi)
+ leal SYS_exit, %eax /* call exit */
+ int $0x80
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .type umult,@function
+ .global umult
+umult:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+
+ movl 8(%ebp), %eax
+ movl 12(%ebp), %ebx
+ mull %ebx
+ movl 16(%ebp), %ebx
+ movl %edx, (%ebx)
+
+ popl %ebx
+ popl %ebp
+ ret
+
+ .type FPsave,@function
+ .global FPsave
+FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .type FPrestore,@function
+ .global FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .type getcallerpc,@function
+ .global getcallerpc
+getcallerpc:
+ movl 4(%ebp), %eax
+ ret
+
+ .type _tas,@function
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/DragonFly/audio.c
@@ -1,0 +1,547 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/soundcard.h>
+
+#define Audio_Mic_Val SOUND_MIXER_MIC
+#define Audio_Linein_Val SOUND_MIXER_LINE
+
+#define Audio_Speaker_Val SOUND_MIXER_SPEAKER
+#define Audio_Headphone_Val SOUND_MIXER_PHONEOUT
+#define Audio_Lineout_Val SOUND_MIXER_VOLUME
+
+#define Audio_Pcm_Val AFMT_S16_LE
+#define Audio_Ulaw_Val AFMT_MU_LAW
+#define Audio_Alaw_Val AFMT_A_LAW
+
+#include "audio-tbls.c"
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+static int debug;
+
+#define AUDIO_FILE_STRING "/dev/dsp"
+
+enum {
+ A_Pause,
+ A_UnPause
+};
+
+enum {
+ A_In,
+ A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static int audio_file = -1; /* file in/out */
+static int audio_file_in = -1; /* copy of above when opened O_READ/O_RDWR */
+static int audio_file_out = -1; /* copy of above when opened O_WRITE/O_RDWR */
+
+static int audio_swap_flag = 0; /* endian swap */
+
+static int audio_in_pause = A_UnPause;
+
+static Audio_t av;
+static int mixerleftvol[32];
+static int mixerrightvol[32];
+
+static int audio_enforce(Audio_t*);
+static int audio_open(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+ int i;
+ static ushort flag = 1;
+
+ audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */
+ audio_info_init(&av);
+ for (i = 0; i < 32; i++)
+ mixerleftvol[i] = mixerrightvol[i] = 100;
+}
+
+void
+audio_ctl_init(void)
+{
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ char ebuf[ERRMAX];
+
+ if (debug)
+ print("audio_file_open(0x%.8lux, %d)\n", c, omode);
+ switch(omode){
+ case OREAD:
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file;
+ poperror();
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ qunlock(&inlock);
+ nexterror();
+ }
+ if(audio_file_in >= 0 || audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+ if (debug)
+ print("audio_file_open: success\nin %d out %d both %d\n",
+ audio_file_out, audio_file_in, audio_file);
+}
+
+void
+audio_ctl_open(Chan *c, int omode)
+{
+ USED(c);
+ USED(omode);
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_out < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ qunlock(&outlock);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_in < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ audio_file_out = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ close(audio_file);
+ audio_file_in = audio_file_out = audio_file = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+}
+
+void
+audio_ctl_close(Chan *c)
+{
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long ba, status, chunk, total;
+ char *pva = (char *) va;
+
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(!audio_pause_in(audio_file_in, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count) {
+ chunk = count - total;
+ osenter();
+ status = read(audio_file_in, pva + total, chunk);
+ osleave();
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(pva, count);
+
+ poperror();
+ qunlock(&inlock);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ if (debug > 1)
+ print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
+ c, va, count, offset);
+
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(audio_file_out < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(va, count);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count) {
+ chunk = min(bufsz, count - total);
+ osenter();
+ status = write(audio_file_out, va, chunk);
+ osleave();
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ return count;
+}
+
+static int
+audio_open(void)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ if (debug)
+ print("audio_open: failed to set blocking\n");
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ if (debug)
+ print("audio_open: blocking set\n");
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = 0;
+
+ if(! audio_set_info(fd, &av.in, A_In)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ int fd;
+ int ff;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if (!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if (!audio_enforce(&tmpav))
+ error(Ebadarg);
+
+ qlock(&inlock);
+ if (waserror()) {
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+ if (!audio_pause_in(audio_file_in, A_Pause))
+ error(Ebadarg);
+ if (!audio_flush(audio_file_in, A_In))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&inlock);
+
+ qlock(&outlock);
+ if (waserror()) {
+ qunlock(&outlock);
+ nexterror();
+ }
+ if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+ if (!audio_pause_out(audio_file_out))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&outlock);
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+doioctl(int fd, int ctl, int *info)
+{
+ int status;
+ osenter();
+ status = ioctl(fd, ctl, info); /* qlock and load general stuff */
+ osleave();
+ if (status < 0)
+ print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
+ return status;
+}
+
+static int
+choosefmt(Audio_d *i)
+{
+ int newbits, newenc;
+
+ newbits = i->bits;
+ newenc = i->enc;
+ switch (newenc) {
+ case Audio_Alaw_Val:
+ if (newbits == 8)
+ return AFMT_A_LAW;
+ break;
+ case Audio_Ulaw_Val:
+ if (newbits == 8)
+ return AFMT_MU_LAW;
+ break;
+ case Audio_Pcm_Val:
+ if (newbits == 8)
+ return AFMT_U8;
+ else if (newbits == 16)
+ return AFMT_S16_LE;
+ break;
+ }
+ return -1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status;
+ int unequal_stereo = 0;
+
+ if(fd < 0)
+ return 0;
+
+ /* fmt */
+ if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
+ int oldfmt, newfmt;
+ oldfmt = AFMT_QUERY;
+ if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
+ return 0;
+ if (debug)
+ print("audio_set_info: current format 0x%.8lux\n", oldfmt);
+ newfmt = choosefmt(i);
+ if (debug)
+ print("audio_set_info: new format 0x%.8lux\n", newfmt);
+ if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
+ return 0;
+ }
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG) {
+ int channels = i->chan;
+ if (debug)
+ print("audio_set_info: new channels %d\n", channels);
+ if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
+ || channels != i->chan)
+ return 0;
+ }
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG) {
+ int speed = i->rate;
+ if (debug)
+ print("audio_set_info: new speed %d\n", speed);
+ if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
+ return 0;
+ }
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
+ int val;
+ if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
+ mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
+ if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
+ mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
+ val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
+ doioctl(fd, MIXER_WRITE(i->dev), &val);
+ }
+
+ if (i->flags & AUDIO_DEV_FLAG) {
+ }
+
+ return 1;
+}
+
+void
+audio_swap_endian(char *p, int n)
+{
+ int b;
+
+ while (n > 1) {
+ b = p[0];
+ p[0] = p[1];
+ p[1] = b;
+ p += 2;
+ n -= 2;
+ }
+}
+
+static int
+audio_pause_out(int fd)
+{
+ USED(fd);
+ return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+ USED(fd);
+ USED(f);
+ return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+ int x;
+ return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+ if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) &&
+ (t->in.rate != 8000 || t->in.chan != 1))
+ return 0;
+ if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) &&
+ (t->out.rate != 8000 || t->out.chan != 1))
+ return 0;
+ return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
--- /dev/null
+++ b/emu/DragonFly/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgid(0, getpid());
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/DragonFly/deveia.c
@@ -1,0 +1,39 @@
+/*
+ * FreeBSD serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/cuaa0",
+ "/dev/cuaa1",
+ "/dev/cuaa2",
+ "/dev/cuaa3",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {115200, B115200},
+ {230400, B230400},
+ {-1, -1}
+};
+
--- /dev/null
+++ b/emu/DragonFly/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/DragonFly/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/DragonFly/emu-g
@@ -1,0 +1,97 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ ip ipif-posix ipaux
+ eia
+ mem
+
+lib
+ interp
+ math
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ void setpointer(int x, int y) { USED(x); USED(y); }
+ ulong strtochan(char *s) { USED(s); return ~0; }
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/DragonFly/mkfile
@@ -1,0 +1,46 @@
+SYSTARG=DragonFly
+OBJTYPE=386
+<../../mkconfig
+SYSTARG=DragonFly
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lm
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/DragonFly/os.c
@@ -1,0 +1,530 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#undef getwd
+#include <signal.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <sys/time.h>
+#include <termios.h>
+#include <sched.h>
+#include <pwd.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+enum
+{
+ DELETE = 0x7F,
+ NSTACKSPERALLOC = 16,
+ X11STACK= 256*1024
+};
+char *hosttype = "DragonFly";
+
+extern void unlockandexit(int*);
+extern void executeonnewstack(void*, void (*f)(void*), void*);
+static void *stackalloc(Proc *p, void **tos);
+static void stackfreeandexit(void *stack);
+
+extern int dflag;
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+ Proc *p;
+ void *kstack;
+
+ lock(&procs.l);
+ p = up;
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(up->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ if(0)
+ print("pexit: %s: %s\n", up->text, msg);
+
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ kstack = p->kstack;
+ free(p->prog);
+ free(p);
+ if(kstack != nil)
+ stackfreeandexit(kstack);
+}
+
+void
+trapBUS(int signo, siginfo_t *info, void *context)
+{
+ if(info)
+ print("trapBUS: signo: %d code: %d addr: %lx\n",
+ info->si_signo, info->si_code, info->si_addr);
+ else
+ print("trapBUS: no info\n");
+ disfault(nil, "Bus error");
+}
+
+static void
+trapUSR1(int signo)
+{
+ int intwait;
+
+ USED(signo);
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+ if(intwait == 0) /* Not posted so its a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+static void
+trapUSR2(int signo)
+{
+ USED(signo);
+ /* we've done our work of interrupting sigsuspend */
+}
+
+static void
+trapILL(int signo)
+{
+ disfault(nil, "Illegal instruction");
+}
+
+static void
+trapSEGV(int signo)
+{
+ disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(int signo)
+{
+ char buf[64];
+ USED(signo);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
+ disfault(nil, buf);
+}
+
+static sigset_t initmask;
+
+static void
+setsigs(void)
+{
+ struct sigaction act;
+ sigset_t mask;
+
+ memset(&act, 0 , sizeof(act));
+ sigemptyset(&initmask);
+
+ signal(SIGPIPE, SIG_IGN); /* prevent signal when devcmd child exits */
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+
+ act.sa_handler = trapUSR1;
+ act.sa_mask = initmask;
+ sigaction(SIGUSR1, &act, nil);
+
+ act.sa_handler = trapUSR2;
+ sigaction(SIGUSR2, &act, nil);
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR2);
+ sigaddset(&initmask, SIGUSR2);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ /*
+ * prevent Zombies forming when any process terminates
+ */
+ act.sa_sigaction = 0;
+ act.sa_flags |= SA_NOCLDWAIT;
+ if(sigaction(SIGCHLD, &act, nil))
+ panic("sigaction SIGCHLD");
+
+ if(sflag == 0) {
+ act.sa_sigaction = trapBUS;
+ act.sa_flags |= SA_SIGINFO;
+ if(sigaction(SIGBUS, &act, nil))
+ panic("sigaction SIGBUS");
+ act.sa_handler = trapILL;
+ if(sigaction(SIGILL, &act, nil))
+ panic("sigaction SIGBUS");
+ act.sa_handler = trapSEGV;
+ if(sigaction(SIGSEGV, &act, nil))
+ panic("sigaction SIGSEGV");
+ act.sa_handler = trapFPE;
+ if(sigaction(SIGFPE, &act, nil))
+ panic("sigaction SIGFPE");
+ if(sigaddset(&initmask, SIGINT) == -1)
+ panic("sigaddset");
+ }
+ if(sigprocmask(SIG_BLOCK, &initmask, nil)!= 0)
+ panic("sigprocmask");
+}
+
+static int
+tramp(void *arg)
+{
+ Proc *p;
+
+ p = arg;
+ p->pid = p->sigid = getpid();
+ sigprocmask(SIG_BLOCK, &initmask, nil); /* in 5.3, rfork_thread doesn't copy from parent, contrary to docs? */
+ (*p->func)(p->arg);
+ pexit("{Tramp}", 0);
+ _exit(0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ int pid;
+ void *tos;
+
+ p = newproc();
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if(flags & KPX11){
+ p->kstack = nil; /* never freed; also up not defined */
+ tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*);
+ }else
+ p->kstack = stackalloc(p, &tos);
+ pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, tos, tramp, p);
+ if(pid < 0)
+ panic("rfork");
+}
+
+void
+oshostintr(Proc *p)
+{
+ kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ sigset_t mask;
+
+ sigprocmask(SIG_SETMASK, NULL, &mask);
+ sigdelset(&mask, SIGUSR2);
+ sigsuspend(&mask);
+}
+
+void
+osready(Proc *p)
+{
+ if(kill(p->sigid, SIGUSR2) < 0)
+ fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody()
+{
+ struct passwd *pwd;
+
+ if(pwd = getpwnam("nobody")) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+libinit(char *imod)
+{
+ struct passwd *pw;
+ Proc *p;
+ void *tos;
+ char sys[64];
+
+ setsid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ getnobody();
+
+ if(dflag == 0)
+ termset();
+
+ setsigs();
+
+ p = newproc();
+ p->kstack = stackalloc(p, &tos);
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ p->env->uid = getuid();
+ p->env->gid = getgid();
+
+ executeonnewstack(tos, emuinit, imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return 0;
+ if(sec0==0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec / 1000;
+ time.tv_nsec = (milsec % 1000) * 1000000;
+ nanosleep(&time, 0);
+ return 0;
+}
+
+void
+osyield(void)
+{
+ sched_yield();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+static struct {
+ Lock l;
+ void *free;
+} stacklist;
+
+static void
+_stackfree(void *stack)
+{
+ *((void **)stack) = stacklist.free;
+ stacklist.free = stack;
+}
+
+static void
+stackfreeandexit(void *stack)
+{
+ lock(&stacklist.l);
+ _stackfree(stack);
+ unlockandexit(&stacklist.l.val);
+}
+
+static void *
+stackalloc(Proc *p, void **tos)
+{
+ void *rv;
+ lock(&stacklist.l);
+ if (stacklist.free == 0) {
+ int x;
+ /*
+ * obtain some more by using sbrk()
+ */
+ void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
+ if (more == 0)
+ panic("stackalloc: no more stacks");
+ /*
+ * align to KSTACK
+ */
+ more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
+ /*
+ * free all the new stacks onto the freelist
+ */
+ for (x = 0; x < NSTACKSPERALLOC; x++)
+ _stackfree((char *)more + KSTACK * x);
+ }
+ rv = stacklist.free;
+ stacklist.free = *(void **)rv;
+ unlock(&stacklist.l);
+ *tos = rv + KSTACK - sizeof(void*);
+ *(Proc **)rv = p;
+ return rv;
+}
+
+int
+segflush(void *a, ulong n)
+{
+ USED(a);
+ USED(n);
+ return 0;
+}
--- /dev/null
+++ b/emu/FreeBSD/asm-386.S
@@ -1,0 +1,107 @@
+ .file "asm-FreeBSD-386.S"
+#include <sys/syscall.h>
+
+/*
+ * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg)
+ */
+
+ .type ournewstack,@function
+ .global executeonnewstack
+executeonnewstack:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+
+ movl 8(%ebp), %esi /* get tos */
+ subl $4, %esi
+ movl 16(%ebp), %eax
+ movl %eax, (%esi) /* stash arg on new stack */
+ subl $4, %esi
+ movl 12(%ebp), %eax
+ movl %eax, (%esi) /* stash tramp on new stack */
+ mov %esi, %esp /* swap stacks pronto */
+ popl %eax /* recover the tramp address */
+ call *%eax /* and jump to it (ho ho) */
+
+ /* if we return here, tramp didn't do it's job */
+
+ addl $8, %esp /* clean up for pose value */
+
+ leal SYS_exit, %eax
+ int $0x80
+
+/*
+ * unlockandexit(int *key)
+ *
+ * NB: the return status may be rubbish if the stack is reused
+ * between the unlock and the system call, but this should
+ * not matter since no task is waiting for the result
+ */
+
+ .type unlockandexit,@function
+ .global unlockandexit
+unlockandexit:
+ pushl %ebp
+ movl %esp, %ebp
+
+ movl 8(%ebp), %esi /* get the key address */
+ pushl $0 /* exit status 0 */
+ movl $0, %eax /* unlock the stack allocator */
+ movl %eax, (%esi)
+ leal SYS_exit, %eax /* call exit */
+ int $0x80
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .type umult,@function
+ .global umult
+umult:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+
+ movl 8(%ebp), %eax
+ movl 12(%ebp), %ebx
+ mull %ebx
+ movl 16(%ebp), %ebx
+ movl %edx, (%ebx)
+
+ popl %ebx
+ popl %ebp
+ ret
+
+ .type FPsave,@function
+ .global FPsave
+FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .type FPrestore,@function
+ .global FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .type getcallerpc,@function
+ .global getcallerpc
+getcallerpc:
+ movl 4(%ebp), %eax
+ ret
+
+ .type _tas,@function
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/FreeBSD/audio.c
@@ -1,0 +1,547 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/soundcard.h>
+
+#define Audio_Mic_Val SOUND_MIXER_MIC
+#define Audio_Linein_Val SOUND_MIXER_LINE
+
+#define Audio_Speaker_Val SOUND_MIXER_SPEAKER
+#define Audio_Headphone_Val SOUND_MIXER_PHONEOUT
+#define Audio_Lineout_Val SOUND_MIXER_VOLUME
+
+#define Audio_Pcm_Val AFMT_S16_LE
+#define Audio_Ulaw_Val AFMT_MU_LAW
+#define Audio_Alaw_Val AFMT_A_LAW
+
+#include "audio-tbls.c"
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+static int debug;
+
+#define AUDIO_FILE_STRING "/dev/dsp"
+
+enum {
+ A_Pause,
+ A_UnPause
+};
+
+enum {
+ A_In,
+ A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static int audio_file = -1; /* file in/out */
+static int audio_file_in = -1; /* copy of above when opened O_READ/O_RDWR */
+static int audio_file_out = -1; /* copy of above when opened O_WRITE/O_RDWR */
+
+static int audio_swap_flag = 0; /* endian swap */
+
+static int audio_in_pause = A_UnPause;
+
+static Audio_t av;
+static int mixerleftvol[32];
+static int mixerrightvol[32];
+
+static int audio_enforce(Audio_t*);
+static int audio_open(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+ int i;
+ static ushort flag = 1;
+
+ audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */
+ audio_info_init(&av);
+ for (i = 0; i < 32; i++)
+ mixerleftvol[i] = mixerrightvol[i] = 100;
+}
+
+void
+audio_ctl_init(void)
+{
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ char ebuf[ERRMAX];
+
+ if (debug)
+ print("audio_file_open(0x%.8lux, %d)\n", c, omode);
+ switch(omode){
+ case OREAD:
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file;
+ poperror();
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ qunlock(&inlock);
+ nexterror();
+ }
+ if(audio_file_in >= 0 || audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+ if (debug)
+ print("audio_file_open: success\nin %d out %d both %d\n",
+ audio_file_out, audio_file_in, audio_file);
+}
+
+void
+audio_ctl_open(Chan *c, int omode)
+{
+ USED(c);
+ USED(omode);
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_out < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ qunlock(&outlock);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_in < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ audio_file_out = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ close(audio_file);
+ audio_file_in = audio_file_out = audio_file = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+}
+
+void
+audio_ctl_close(Chan *c)
+{
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long ba, status, chunk, total;
+ char *pva = (char *) va;
+
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(! audio_pause_in(audio_file_in, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count) {
+ chunk = count - total;
+ osenter();
+ status = read(audio_file_in, pva + total, chunk);
+ osleave();
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(pva, count);
+
+ poperror();
+ qunlock(&inlock);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ if (debug > 1)
+ print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
+ c, va, count, offset);
+
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(audio_file_out < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(va, count);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count) {
+ chunk = min(bufsz, count - total);
+ osenter();
+ status = write(audio_file_out, va, chunk);
+ osleave();
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ return count;
+}
+
+static int
+audio_open(void)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ if (debug)
+ print("audio_open: failed to set blocking\n");
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ if (debug)
+ print("audio_open: blocking set\n");
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = 0;
+
+ if(! audio_set_info(fd, &av.in, A_In)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ int fd;
+ int ff;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if (!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if (!audio_enforce(&tmpav))
+ error(Ebadarg);
+
+ qlock(&inlock);
+ if (waserror()) {
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+ if (!audio_pause_in(audio_file_in, A_Pause))
+ error(Ebadarg);
+ if (!audio_flush(audio_file_in, A_In))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&inlock);
+
+ qlock(&outlock);
+ if (waserror()) {
+ qunlock(&outlock);
+ nexterror();
+ }
+ if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+ if (!audio_pause_out(audio_file_out))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&outlock);
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+doioctl(int fd, int ctl, int *info)
+{
+ int status;
+ osenter();
+ status = ioctl(fd, ctl, info); /* qlock and load general stuff */
+ osleave();
+ if (status < 0)
+ print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
+ return status;
+}
+
+static int
+choosefmt(Audio_d *i)
+{
+ int newbits, newenc;
+
+ newbits = i->bits;
+ newenc = i->enc;
+ switch (newenc) {
+ case Audio_Alaw_Val:
+ if (newbits == 8)
+ return AFMT_A_LAW;
+ break;
+ case Audio_Ulaw_Val:
+ if (newbits == 8)
+ return AFMT_MU_LAW;
+ break;
+ case Audio_Pcm_Val:
+ if (newbits == 8)
+ return AFMT_U8;
+ else if (newbits == 16)
+ return AFMT_S16_LE;
+ break;
+ }
+ return -1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status;
+ int unequal_stereo = 0;
+
+ if(fd < 0)
+ return 0;
+
+ /* fmt */
+ if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
+ int oldfmt, newfmt;
+ oldfmt = AFMT_QUERY;
+ if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
+ return 0;
+ if (debug)
+ print("audio_set_info: current format 0x%.8lux\n", oldfmt);
+ newfmt = choosefmt(i);
+ if (debug)
+ print("audio_set_info: new format 0x%.8lux\n", newfmt);
+ if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
+ return 0;
+ }
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG) {
+ int channels = i->chan;
+ if (debug)
+ print("audio_set_info: new channels %d\n", channels);
+ if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
+ || channels != i->chan)
+ return 0;
+ }
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG) {
+ int speed = i->rate;
+ if (debug)
+ print("audio_set_info: new speed %d\n", speed);
+ if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
+ return 0;
+ }
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
+ int val;
+ if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
+ mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
+ if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
+ mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
+ val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
+ doioctl(fd, MIXER_WRITE(i->dev), &val);
+ }
+
+ if (i->flags & AUDIO_DEV_FLAG) {
+ }
+
+ return 1;
+}
+
+void
+audio_swap_endian(char *p, int n)
+{
+ int b;
+
+ while (n > 1) {
+ b = p[0];
+ p[0] = p[1];
+ p[1] = b;
+ p += 2;
+ n -= 2;
+ }
+}
+
+static int
+audio_pause_out(int fd)
+{
+ USED(fd);
+ return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+ USED(fd);
+ USED(f);
+ return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+ int x;
+ return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+ if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) &&
+ (t->in.rate != 8000 || t->in.chan != 1))
+ return 0;
+ if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) &&
+ (t->out.rate != 8000 || t->out.chan != 1))
+ return 0;
+ return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
--- /dev/null
+++ b/emu/FreeBSD/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgid(0, getpid());
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/FreeBSD/deveia.c
@@ -1,0 +1,39 @@
+/*
+ * FreeBSD serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/cuaa0",
+ "/dev/cuaa1",
+ "/dev/cuaa2",
+ "/dev/cuaa3",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {115200, B115200},
+ {230400, B230400},
+ {-1, -1}
+};
+
--- /dev/null
+++ b/emu/FreeBSD/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/FreeBSD/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/FreeBSD/emu-g
@@ -1,0 +1,102 @@
+env
+ X11LIBS=
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ math
+
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ void setpointer(int x, int y){USED(x); USED(y);}
+ ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/FreeBSD/mkfile
@@ -1,0 +1,48 @@
+SYSTARG=FreeBSD
+OBJTYPE=386
+<../../mkconfig
+SYSTARG=FreeBSD
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+X11LIBS= -lX11 -lXext
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lm $X11LIBS
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/FreeBSD/mkfile-FreeBSD
@@ -1,0 +1,17 @@
+#
+# architecture-dependent files for FreeBSD
+#
+
+LDFLAGS=
+
+TARGFILES=devfs-posix.$O\
+ deveia-FreeBSD.$O\
+ devip.$O\
+ ipif-posix.$O\
+ os-FreeBSD.$O\
+ win-x11.$O\
+ srv.$O\
+ lock.$O\
+ asm-FreeBSD-$OBJTYPE.$O
+
+SYSLIBS=/usr/X11R6/lib/libX11.a -lm
--- /dev/null
+++ b/emu/FreeBSD/os.c
@@ -1,0 +1,531 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#undef getwd
+#include <signal.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <sys/time.h>
+#include <termios.h>
+#include <sched.h>
+#include <pwd.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+enum
+{
+ DELETE = 0x7F,
+ NSTACKSPERALLOC = 16,
+ X11STACK= 256*1024
+};
+char *hosttype = "FreeBSD";
+
+extern void unlockandexit(int*);
+extern void executeonnewstack(void*, void (*f)(void*), void*);
+static void *stackalloc(Proc *p, void **tos);
+static void stackfreeandexit(void *stack);
+
+extern int dflag;
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+ Proc *p;
+ void *kstack;
+
+ lock(&procs.l);
+ p = up;
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(up->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ if(0)
+ print("pexit: %s: %s\n", up->text, msg);
+
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ kstack = p->kstack;
+ free(p->prog);
+ free(p);
+ if(kstack != nil)
+ stackfreeandexit(kstack);
+}
+
+void
+trapBUS(int signo, siginfo_t *info, void *context)
+{
+ if(info)
+ print("trapBUS: signo: %d code: %d addr: %lx\n",
+ info->si_signo, info->si_code, info->si_addr);
+ else
+ print("trapBUS: no info\n");
+ disfault(nil, "Bus error");
+}
+
+static void
+trapUSR1(int signo)
+{
+ int intwait;
+
+ USED(signo);
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+ if(intwait == 0) /* Not posted so its a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+static void
+trapUSR2(int signo)
+{
+ USED(signo);
+ /* we've done our work of interrupting sigsuspend */
+}
+
+static void
+trapILL(int signo)
+{
+ disfault(nil, "Illegal instruction");
+}
+
+static void
+trapSEGV(int signo)
+{
+ disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(int signo)
+{
+
+ char buf[64];
+ USED(signo);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
+ disfault(nil, buf);
+}
+
+static sigset_t initmask;
+
+static void
+setsigs(void)
+{
+ struct sigaction act;
+ sigset_t mask;
+
+ memset(&act, 0 , sizeof(act));
+ sigemptyset(&initmask);
+
+ signal(SIGPIPE, SIG_IGN); /* prevent signal when devcmd child exits */
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+
+ act.sa_handler = trapUSR1;
+ act.sa_mask = initmask;
+ sigaction(SIGUSR1, &act, nil);
+
+ act.sa_handler = trapUSR2;
+ sigaction(SIGUSR2, &act, nil);
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR2);
+ sigaddset(&initmask, SIGUSR2);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ /*
+ * prevent Zombies forming when any process terminates
+ */
+ act.sa_sigaction = 0;
+ act.sa_flags |= SA_NOCLDWAIT;
+ if(sigaction(SIGCHLD, &act, nil))
+ panic("sigaction SIGCHLD");
+
+ if(sflag == 0) {
+ act.sa_sigaction = trapBUS;
+ act.sa_flags |= SA_SIGINFO;
+ if(sigaction(SIGBUS, &act, nil))
+ panic("sigaction SIGBUS");
+ act.sa_handler = trapILL;
+ if(sigaction(SIGILL, &act, nil))
+ panic("sigaction SIGBUS");
+ act.sa_handler = trapSEGV;
+ if(sigaction(SIGSEGV, &act, nil))
+ panic("sigaction SIGSEGV");
+ act.sa_handler = trapFPE;
+ if(sigaction(SIGFPE, &act, nil))
+ panic("sigaction SIGFPE");
+ if(sigaddset(&initmask, SIGINT) == -1)
+ panic("sigaddset");
+ }
+ if(sigprocmask(SIG_BLOCK, &initmask, nil)!= 0)
+ panic("sigprocmask");
+}
+
+static int
+tramp(void *arg)
+{
+ Proc *p;
+
+ p = arg;
+ p->pid = p->sigid = getpid();
+ sigprocmask(SIG_BLOCK, &initmask, nil); /* in 5.3, rfork_thread doesn't copy from parent, contrary to docs? */
+ (*p->func)(p->arg);
+ pexit("{Tramp}", 0);
+ _exit(0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ int pid;
+ void *tos;
+
+ p = newproc();
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if(flags & KPX11){
+ p->kstack = nil; /* never freed; also up not defined */
+ tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*);
+ }else
+ p->kstack = stackalloc(p, &tos);
+ pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, tos, tramp, p);
+ if(pid < 0)
+ panic("rfork");
+}
+
+void
+oshostintr(Proc *p)
+{
+ kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ sigset_t mask;
+
+ sigprocmask(SIG_SETMASK, NULL, &mask);
+ sigdelset(&mask, SIGUSR2);
+ sigsuspend(&mask);
+}
+
+void
+osready(Proc *p)
+{
+ if(kill(p->sigid, SIGUSR2) < 0)
+ fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody()
+{
+ struct passwd *pwd;
+
+ if(pwd = getpwnam("nobody")) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+libinit(char *imod)
+{
+ struct passwd *pw;
+ Proc *p;
+ void *tos;
+ char sys[64];
+
+ setsid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ getnobody();
+
+ if(dflag == 0)
+ termset();
+
+ setsigs();
+
+ p = newproc();
+ p->kstack = stackalloc(p, &tos);
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ p->env->uid = getuid();
+ p->env->gid = getgid();
+
+ executeonnewstack(tos, emuinit, imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return 0;
+ if(sec0==0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec / 1000;
+ time.tv_nsec = (milsec % 1000) * 1000000;
+ nanosleep(&time, 0);
+ return 0;
+}
+
+void
+osyield(void)
+{
+ sched_yield();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+static struct {
+ Lock l;
+ void *free;
+} stacklist;
+
+static void
+_stackfree(void *stack)
+{
+ *((void **)stack) = stacklist.free;
+ stacklist.free = stack;
+}
+
+static void
+stackfreeandexit(void *stack)
+{
+ lock(&stacklist.l);
+ _stackfree(stack);
+ unlockandexit(&stacklist.l.val);
+}
+
+static void *
+stackalloc(Proc *p, void **tos)
+{
+ void *rv;
+ lock(&stacklist.l);
+ if (stacklist.free == 0) {
+ int x;
+ /*
+ * obtain some more by using sbrk()
+ */
+ void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
+ if (more == 0)
+ panic("stackalloc: no more stacks");
+ /*
+ * align to KSTACK
+ */
+ more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
+ /*
+ * free all the new stacks onto the freelist
+ */
+ for (x = 0; x < NSTACKSPERALLOC; x++)
+ _stackfree((char *)more + KSTACK * x);
+ }
+ rv = stacklist.free;
+ stacklist.free = *(void **)rv;
+ unlock(&stacklist.l);
+ *tos = rv + KSTACK - sizeof(void*);
+ *(Proc **)rv = p;
+ return rv;
+}
+
+int
+segflush(void *a, ulong n)
+{
+ USED(a);
+ USED(n);
+ return 0;
+}
--- /dev/null
+++ b/emu/Irix/NOTE
@@ -1,0 +1,1 @@
+for some strange reason, it lacks deveia!
--- /dev/null
+++ b/emu/Irix/asm-mips.s
@@ -1,0 +1,31 @@
+#include <sys/regdef.h>
+#include <sys/asm.h>
+
+LEAF(FPsave)
+ cfc1 t0, $31
+ sw t0, 0(a0) /* a0 is argument */
+ j $31
+ END(FPsave)
+
+LEAF(FPrestore)
+ lw t0, 0(a0) /* a0 is argument */
+ ctc1 t0, $31
+ j $31
+ END(FPrestore)
+
+
+/*
+ * lock from r4000 book
+ */
+LEAF(_tas)
+ .set noreorder
+1:
+ ll v0,0(a0) /* a0 is argument */
+ or t1, v0, 1
+ sc t1,0(a0)
+ beq t1,zero,1b
+ nop
+ j $31 /* lock held */
+ nop
+ .set reorder
+ END(_tas)
--- /dev/null
+++ b/emu/Irix/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgrp();
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/Irix/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/Irix/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+# eia
+# audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Irix/mkfile
@@ -1,0 +1,49 @@
+SYSTARG=Irix
+OBJTYPE=mips
+<../../mkconfig
+SYSTARG=Irix
+OBJTYPE=mips
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+LIBNAMES=${LIBS:%=lib%.a}
+#libs=${LIBS:%=$ROOT/$OBJDIR/lib/lib%.a}
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lfpe -lm -lX11 -lXext
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+<../port/portmkfile
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/Irix/mkfile-Irix
@@ -1,0 +1,14 @@
+#
+# architecture-dependent files for IRIX
+#
+
+TARGFILES=asm-Irix.$O\
+ devfs-posix.$O\
+ devip.$O\
+ ipif-posix.$O\
+ os-Irix.$O\
+ win-x11.$O\
+ srv.$O\
+ lock.$O\
+
+SYSLIBS= -lfpe -lm -lX11
--- /dev/null
+++ b/emu/Irix/os.c
@@ -1,0 +1,475 @@
+/* Link with -lfpe. See man pages for fpc
+ * and /usr/include/sigfpe.h, sys/fpu.h.
+ */
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <time.h>
+#include <ulocks.h>
+#include <termios.h>
+#include <sigfpe.h>
+#include <sys/prctl.h>
+#include <sys/fpu.h>
+#include <sys/cachectl.h>
+#undef _POSIX_SOURCE /* SGI incompetence */
+#include <signal.h>
+#define _BSD_TIME
+/* for gettimeofday(), which isn't POSIX,
+ * but is fairly common
+ */
+#include <sys/time.h>
+#define _POSIX_SOURCE
+#include <pwd.h>
+
+extern int rebootargc;
+extern char** rebootargv;
+
+int gidnobody = -1;
+int uidnobody = -1;
+Proc** Xup;
+
+#define MAXSPROC 30000 /* max procs == MAXPID */
+static int sproctbl[MAXSPROC];
+
+enum
+{
+ KSTACK = 64*1024,
+ DELETE = 0x7F
+};
+char *hosttype = "Irix";
+char *cputype = "mips";
+
+extern int dflag;
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+
+ lock(&procs.l);
+ if(up->prev)
+ up->prev->next = up->next;
+ else
+ procs.head = up->next;
+
+ if(up->next)
+ up->next->prev = up->prev;
+ else
+ procs.tail = up->prev;
+
+ sproctbl[getpid()] = -1;
+
+ unlock(&procs.l);
+
+/* print("pexit: %s: %s\n", up->text, msg); /**/
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(up->prog);
+ free(up);
+ exit(0);
+}
+
+static void
+tramp(void *p, size_t stacksz)
+{
+ up = p;
+ up->sigid = getpid();
+ up->func(up->arg);
+ pexit("", 0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ int pid;
+ int id;
+ int i;
+
+ p = newproc();
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+
+ for(i = 1; i < MAXSPROC; i++) {
+ if(sproctbl[i] == -1) {
+ break;
+ }
+ }
+
+ if(i==MAXSPROC)
+ return -1;
+
+ sproctbl[i] = -i - 1; /* temporary hold of table index outside of lock */
+
+ unlock(&procs.l);
+
+ pid = sprocsp(tramp, PR_SALL, p, 0, KSTACK);
+
+ if(pid >= 0)
+ sproctbl[i] = pid;
+ else
+ sproctbl[i] = -1;
+}
+
+void
+osblock(void)
+{
+ blockproc(up->sigid);
+}
+
+void
+osready(Proc *p)
+{
+ unblockproc(p->sigid);
+}
+
+void
+trapUSR1(void)
+{
+ int intwait;
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+
+ if(intwait == 0) /* Not posted so it's a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+void
+trapILL(void)
+{
+ disfault(nil, "Illegal instruction");
+}
+
+void
+trapBUS(void)
+{
+ disfault(nil, "Bus error");
+}
+
+void
+trapSEGV(void)
+{
+ disfault(nil, "Segmentation violation");
+}
+
+/*
+ * This is not a signal handler but rather a vector from real/FPcontrol-Irix.c
+ */
+void
+trapFPE(unsigned exception[5], int value[2])
+{
+ disfault(nil, "Floating point exception");
+}
+
+void
+oshostintr(Proc *p)
+{
+ kill(p->sigid, SIGUSR1);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+static struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+
+/* if(sproctbl[0] < 0)
+ panic("corrupt sproc tbl");
+
+ kill(sproctbl[0], SIGUSR2);
+ sginap(10000); */
+}
+
+void
+trapUSR2(void)
+{
+ int i;
+
+ for(i = MAXSPROC - 1; i > 0; i--) {
+ if(sproctbl[i] != -1)
+ kill(sproctbl[i], SIGKILL);
+ sproctbl[i] = -1;
+ }
+
+ execvp(rebootargv[0], rebootargv);
+ panic("reboot failure");
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+getnobody(void)
+{
+ struct passwd *pwd;
+
+ pwd = getpwnam("nobody");
+ if(pwd != nil) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+ struct sigaction act;
+ struct passwd *pw;
+ int i;
+ char sys[64];
+
+ setsid();
+
+ for(i=0; i<MAXSPROC; i++)
+ sproctbl[i] = -1;
+
+ sproctbl[0] = getpid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+
+ if(dflag == 0)
+ termset();
+
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+ signal(SIGUSR2, trapUSR2);
+ /* For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGCLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+ memset(&act, 0 , sizeof(act));
+ act.sa_handler=trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+ if(sflag == 0) {
+ act.sa_handler=trapBUS;
+ sigaction(SIGBUS, &act, nil);
+ act.sa_handler=trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_handler=trapSEGV;
+ sigaction(SIGSEGV, &act, nil);
+ }
+
+ if(usconfig(CONF_INITUSERS, 1000) < 0)
+ panic("usconfig");
+
+ Xup = (Proc**)PRDA->usr_prda.fill;
+ up = newproc();
+
+ pw = getpwuid(getuid());
+ if(pw != nil) {
+ if (strlen(pw->pw_name) + 1 <= KNAMELEN)
+ strcpy(eve, pw->pw_name);
+ else
+ print("pw_name too long\n");
+ }
+ else
+ print("cannot getpwuid\n");
+
+ /* after setting up, since this takes locks */
+ getnobody();
+ up->env->uid = getuid();
+ up->env->gid = getgid();
+ emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ fprint(2, "keyboard read error: %s\n", strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+int
+segflush(void *a, ulong n)
+{
+ cacheflush(a, n, BCACHE);
+ return 0;
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return(0);
+ if(sec0==0){
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ static int tick;
+
+ /*
+ * Posix-conforming CLK_TCK implementations tend to call sysconf,
+ * and we don't need the overhead.
+ */
+ if(tick == 0)
+ tick = CLK_TCK;
+ sginap((tick*milsec)/1000);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ sginap(0);
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ nice(2);
+}
--- /dev/null
+++ b/emu/Linux/arm-tas-v5.S
@@ -1,0 +1,18 @@
+
+ .file "arm-tas-v5.S"
+/*
+ * ulong _tas(ulong*);
+ */
+ .align 2
+ .global _tas
+ .type _tas, %function
+_tas:
+ @ args = 0, pretend = 0, frame = 0
+ @ frame_needed = 0, uses_anonymous_args = 0
+ @ link register save eliminated.
+ @ lr needed for prologue
+ mov r3, #1
+ mov r1, r0
+ swp r0, r3, [r1]
+ bx lr
+ .size _tas, .-_tas
--- /dev/null
+++ b/emu/Linux/arm-tas-v7.S
@@ -1,0 +1,30 @@
+ .file "arm-tas-v7.S"
+#ifndef ARMv7
+#define DMB mcr p15, 0, r0, c7, c10, 5
+#else
+#define DMB dmb
+#endif
+.align 2
+.global _tas
+.type _tas, %function
+_tas:
+ @ args = 0, pretend = 0, frame = 0
+ @ frame_needed = 0, uses_anonymous_args = 0
+ @ link register save eliminated.
+ @ lr needed for prologue
+ DMB
+ mov r1, r0
+ mov r2, #0xaa
+tas1:
+ ldrex r0, [r1]
+ cmp r0, #0
+ bne lockbusy
+ strex r3, r2, [r1]
+ cmp r3, #0
+ bne tas1
+ DMB
+ bx lr
+lockbusy:
+ clrex
+ bx lr
+ .size _tas, .-_tas
--- /dev/null
+++ b/emu/Linux/asm-386.S
@@ -1,0 +1,51 @@
+ .file "asm-Linux-386.S"
+ .text
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .type umult,@function
+ .global umult
+umult:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+
+ movl 8(%ebp), %eax
+ movl 12(%ebp), %ebx
+ mull %ebx
+ movl 16(%ebp), %ebx
+ movl %edx, (%ebx)
+
+ popl %ebx
+ popl %ebp
+ ret
+
+ .type FPsave,@function
+ .global FPsave
+FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .type FPrestore,@function
+ .global FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .type _tas,@function
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/Linux/asm-arm.S
@@ -1,0 +1,62 @@
+ .file "asm-Linux-arm.S"
+ .text
+
+/*
+ * ulong umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .align 2
+ .global umult
+ .type umult, %function
+umult:
+ @ args = 0, pretend = 0, frame = 12
+ @ frame_needed = 1, uses_anonymous_args = 0
+ mov ip, sp
+ stmfd sp!, {fp, ip, lr, pc}
+ sub fp, ip, #4
+ sub sp, sp, #12
+ str r0, [fp, #-16]
+ str r1, [fp, #-20]
+ str r2, [fp, #-24]
+ ldr r1, [fp, #-16]
+ ldr r2, [fp, #-20]
+ umull r0, r3, r1, r2
+ ldr r1, [fp, #-24]
+ str r3, [r1]
+ ldmea fp, {fp, sp, pc}
+ .size umult, .-umult
+
+/*
+ * void FPsave(void*);
+ */
+
+ .align 2
+ .global FPsave
+ .type FPsave, %function
+FPsave:
+ @ args = 0, pretend = 0, frame = 4
+ @ frame_needed = 1, uses_anonymous_args = 0
+ mov ip, sp
+ stmfd sp!, {fp, ip, lr, pc}
+ sub fp, ip, #4
+ sub sp, sp, #4
+ str r0, [fp, #-16]
+ ldmea fp, {fp, sp, pc}
+ .size FPsave, .-FPsave
+
+/*
+ * void FPrestore(void*);
+ */
+ .align 2
+ .global FPrestore
+ .type FPrestore, %function
+FPrestore:
+ @ args = 0, pretend = 0, frame = 4
+ @ frame_needed = 1, uses_anonymous_args = 0
+ mov ip, sp
+ stmfd sp!, {fp, ip, lr, pc}
+ sub fp, ip, #4
+ sub sp, sp, #4
+ str r0, [fp, #-16]
+ ldmea fp, {fp, sp, pc}
+ .size FPrestore, .-FPrestore
--- /dev/null
+++ b/emu/Linux/asm-mips.S
@@ -1,0 +1,28 @@
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+LEAF(FPsave)
+ cfc1 t0, $31
+ sw t0, 0(a0) /* a0 is argument */
+ j $31
+ END(FPsave)
+
+LEAF(FPrestore)
+ lw t0, 0(a0) /* a0 is argument */
+ ctc1 t0, $31
+ j $31
+ END(FPrestore)
+
+LEAF(_tas)
+ .set noreorder
+1:
+ ll v0,0(a0) /* a0 is argument */
+ or t1, v0, 1
+ sc t1,0(a0)
+ beq t1,zero,1b
+ nop
+ j $31 /* lock held */
+ nop
+ .set reorder
+ END(_tas)
--- /dev/null
+++ b/emu/Linux/asm-power.S
@@ -1,0 +1,61 @@
+ .align 2
+ .global FPsave
+FPsave:
+ stfd %f14,0*8(%r3)
+ stfd %f15,1*8(%r3)
+ stfd %f16,2*8(%r3)
+ stfd %f17,3*8(%r3)
+ stfd %f18,4*8(%r3)
+ stfd %f19,5*8(%r3)
+ stfd %f20,6*8(%r3)
+ stfd %f21,7*8(%r3)
+ stfd %f22,8*8(%r3)
+ stfd %f23,9*8(%r3)
+ stfd %f24,10*8(%r3)
+ stfd %f25,11*8(%r3)
+ stfd %f26,12*8(%r3)
+ stfd %f27,13*8(%r3)
+ stfd %f28,14*8(%r3)
+ stfd %f29,15*8(%r3)
+ stfd %f30,16*8(%r3)
+ stfd %f31,17*8(%r3)
+ blr
+
+ .align 2
+ .global FPrestore
+FPrestore:
+ lfd %f14,0*8(%r3)
+ lfd %f15,1*8(%r3)
+ lfd %f16,2*8(%r3)
+ lfd %f17,3*8(%r3)
+ lfd %f18,4*8(%r3)
+ lfd %f19,5*8(%r3)
+ lfd %f20,6*8(%r3)
+ lfd %f21,7*8(%r3)
+ lfd %f22,8*8(%r3)
+ lfd %f23,9*8(%r3)
+ lfd %f24,10*8(%r3)
+ lfd %f25,11*8(%r3)
+ lfd %f26,12*8(%r3)
+ lfd %f27,13*8(%r3)
+ lfd %f28,14*8(%r3)
+ lfd %f29,15*8(%r3)
+ lfd %f30,16*8(%r3)
+ lfd %f31,17*8(%r3)
+ blr
+
+ .align 2
+ .global _tas
+_tas:
+ sync
+ mr %r4, %r3
+ addi %r5,0,0x1
+1:
+ lwarx %r3, 0, %r4
+ cmpwi %r3, 0
+ bne- 2f
+ stwcx. %r5, 0, %r4
+ bne- 1b
+2:
+ sync
+ blr
--- /dev/null
+++ b/emu/Linux/asm-spim.S
@@ -1,0 +1,29 @@
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+
+LEAF(FPsave)
+ cfc1 t0, $31
+ sw t0, 0(a0) /* a0 is argument */
+ j $31
+ END(FPsave)
+
+LEAF(FPrestore)
+ lw t0, 0(a0) /* a0 is argument */
+ ctc1 t0, $31
+ j $31
+ END(FPrestore)
+
+LEAF(_tas)
+ .set noreorder
+1:
+ ll v0,0(a0) /* a0 is argument */
+ or t1, v0, 1
+ sc t1,0(a0)
+ beq t1,zero,1b
+ nop
+ j $31 /* lock held */
+ nop
+ .set reorder
+ END(_tas)
--- /dev/null
+++ b/emu/Linux/audio-oss.c
@@ -1,0 +1,441 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "audio.h"
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#define Audio_Mic_Val SOUND_MIXER_MIC
+#define Audio_Linein_Val SOUND_MIXER_LINE
+
+#define Audio_Speaker_Val SOUND_MIXER_PCM // SOUND_MIXER_VOLUME
+#define Audio_Headphone_Val SOUND_MIXER_ALTPCM
+#define Audio_Lineout_Val SOUND_MIXER_CD
+
+#define Audio_Pcm_Val AFMT_S16_LE
+#define Audio_Ulaw_Val AFMT_MU_LAW
+#define Audio_Alaw_Val AFMT_A_LAW
+
+#include "audio-tbls.c"
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+#define DEVAUDIO "/dev/dsp"
+#define DEVMIXER "/dev/mixer"
+
+#define DPRINT if(1)print
+
+enum {
+ A_Pause,
+ A_UnPause,
+ A_In,
+ A_Out,
+};
+
+static struct {
+ int data; /* dsp data fd */
+ int ctl; /* mixer fd */
+ int pause;
+ QLock lk;
+} afd = {.data = -1, .ctl = -1, .pause =A_UnPause };
+
+static Audio_t av;
+static QLock inlock;
+static QLock outlock;
+
+static int audio_open(int);
+static int audio_pause(int, int);
+static int audio_set_info(int, Audio_d*, int);
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
+
+void
+audio_file_init(void)
+{
+ audio_info_init(&av);
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ USED(c);
+ DPRINT("audio_file_open %d %d %x\n", afd.data, afd.ctl, omode);
+ qlock(&afd.lk);
+ if(waserror()){
+ qunlock(&afd.lk);
+ nexterror();
+ }
+ if(afd.data >= 0)
+ error(Einuse);
+ if(afd.ctl < 0){
+ afd.ctl = open(DEVMIXER, ORDWR);
+ if(afd.ctl < 0)
+ oserror();
+ }
+ afd.data = audio_open(omode);
+ if(afd.data < 0)
+ oserror();
+ poperror();
+ qunlock(&afd.lk);
+}
+
+void
+audio_file_close(Chan *c)
+{
+ USED(c);
+ DPRINT("audio_file_close %d %d\n", afd.data, afd.ctl);
+ qlock(&afd.lk);
+ if(waserror()){
+ qunlock(&afd.lk);
+ nexterror();
+ }
+ close(afd.data);
+ afd.data = -1;
+ qunlock(&afd.lk);
+ poperror();
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ long ba, status, chunk, total;
+
+ USED(c);
+ USED(offset);
+ DPRINT("audio_file_read %d %d\n", afd.data, afd.ctl);
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(afd.data < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(!audio_pause(afd.data, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count){
+ chunk = count - total;
+ status = read (afd.data, va + total, chunk);
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ poperror();
+ qunlock(&inlock);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ USED(c);
+ USED(offset);
+ DPRINT("audio_file_write %d %d\n", afd.data, afd.ctl);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(afd.data < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count){
+ chunk = min(bufsz, count - total);
+ status = write(afd.data, va, chunk);
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ return count;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ Audio_t tmpav = av;
+ int tfd;
+
+ USED(c);
+ USED(offset);
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ DPRINT ("audio_ctl_write %X %X\n", afd.data, afd.ctl);
+ if(!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if(!canqlock(&inlock))
+ error("device busy");
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+ if(!canqlock(&outlock))
+ error("device busy");
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ /* DEVAUDIO needs to be open to issue an ioctl */
+ tfd = afd.data;
+ if(tfd < 0){
+ tfd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK);
+ if(tfd < 0)
+ oserror();
+ }
+ if(waserror()){
+ if(tfd != afd.data)
+ close(tfd);
+ nexterror();
+ }
+
+ if(tmpav.in.flags & AUDIO_MOD_FLAG){
+ if(!audio_pause(tfd, A_Pause))
+ error(Ebadarg);
+ if(!audio_set_info(tfd, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+
+ poperror();
+ if(tfd != afd.data)
+ close(tfd);
+
+ tmpav.in.flags = 0;
+ av = tmpav;
+
+ poperror();
+ qunlock(&outlock);
+ poperror();
+ qunlock(&inlock);
+ return count;
+}
+
+/* Linux/OSS specific stuff */
+
+static int
+choosefmt(Audio_d *i)
+{
+ switch(i->bits){
+ case 8:
+ switch(i->enc){
+ case Audio_Alaw_Val:
+ return AFMT_A_LAW;
+ case Audio_Ulaw_Val:
+ return AFMT_MU_LAW;
+ case Audio_Pcm_Val:
+ return AFMT_U8;
+ }
+ break;
+ case 16:
+ if(i->enc == Audio_Pcm_Val)
+ return AFMT_S16_LE;
+ break;
+ }
+ return -1;
+}
+
+static int
+setvolume(int fd, int what, int left, int right)
+{
+ int can, v;
+
+ if(ioctl(fd, SOUND_MIXER_READ_DEVMASK, &can) < 0)
+ can = ~0;
+
+ DPRINT("setvolume fd%d %X can mix 0x%X (mask %X)\n", fd, what, (can & (1<<what)), can);
+ if(!(can & (1<<what)))
+ return 0;
+ v = left | (right<<8);
+ if(ioctl(afd.ctl, MIXER_WRITE(what), &v) < 0)
+ return 0;
+ return 1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status, arg;
+ int oldfmt, newfmt;
+
+ USED(d);
+ DPRINT("audio_set_info (%d) %d %d\n", fd, afd.data, afd.ctl);
+ if(fd < 0)
+ return 0;
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG){
+ arg = i->rate;
+ if(ioctl(fd, SNDCTL_DSP_SPEED, &arg) < 0)
+ return 0;
+ }
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG){
+ arg = i->chan;
+ if(ioctl(fd, SNDCTL_DSP_CHANNELS, &arg) < 0)
+ return 0;
+ }
+
+ /* precision */
+ if(i->flags & AUDIO_BITS_FLAG){
+ arg = i->bits;
+ if(ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &arg) < 0)
+ return 0;
+ }
+
+ /* encoding */
+ if(i->flags & AUDIO_ENC_FLAG){
+ ioctl(fd, SNDCTL_DSP_GETFMTS, &oldfmt);
+
+ newfmt = choosefmt(i);
+ if(newfmt < 0)
+ return 0;
+ if(newfmt != oldfmt){
+ status = ioctl(fd, SNDCTL_DSP_SETFMT, &arg);
+ DPRINT ("enc oldfmt newfmt %x status %d\n", oldfmt, newfmt, status);
+ }
+ }
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG|AUDIO_VOL_FLAG))
+ return setvolume(afd.ctl, i->dev, i->left, i->right);
+
+ return 1;
+}
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+audio_open(int omode)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ switch (omode){
+ case OREAD:
+ fd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK);
+ break;
+ case OWRITE:
+ fd = open(DEVAUDIO, O_WRONLY|O_NONBLOCK);
+ break;
+ case ORDWR:
+ fd = open(DEVAUDIO, O_RDWR|O_NONBLOCK);
+ break;
+ }
+
+ DPRINT("audio_open %d\n", fd);
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)){
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ if(!audio_pause(fd, A_Pause)){
+ close(fd);
+ error(Eio);
+ }
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = ~0;
+
+ if(!audio_set_info(fd, &av.in, A_In)){
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+static int
+dspsync(int fd)
+{
+ return ioctl(fd, SNDCTL_DSP_RESET, NULL) >= 0 &&
+ ioctl(fd, SNDCTL_DSP_SYNC, NULL) >= 0;
+}
+
+static int
+audio_pause(int fd, int f)
+{
+ int status;
+
+// DPRINT ("audio_pause (%d) %d %d\n", fd, afd.data, afd.ctl);
+ if(fd < 0)
+ return 0;
+ if(fd != afd.data)
+ return dspsync(fd);
+ qlock(&afd.lk);
+ if(afd.pause == f){
+ qunlock(&afd.lk);
+ return 1;
+ }
+ if(waserror()){
+ qunlock(&afd.lk);
+ nexterror();
+ }
+ status = dspsync(afd.data);
+ if(status)
+ afd.pause = f;
+ poperror();
+ qunlock(&afd.lk);
+ return status;
+}
--- /dev/null
+++ b/emu/Linux/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgrp();
+ if(nice)
+ setpriority(PRIO_PROCESS, 0, 19);
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/Linux/deveia.c
@@ -1,0 +1,44 @@
+/*
+ * Linux serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/ttyS0",
+ "/dev/ttyS1",
+ "/dev/ttyS2",
+ "/dev/ttyS3",
+ "/dev/ttyS4",
+ "/dev/ttyS5",
+ "/dev/ttyS6",
+ "/dev/ttyS7",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {115200, B115200},
+ {230400, B230400},
+ {460800, B460800},
+ {-1, -1}
+};
+
--- /dev/null
+++ b/emu/Linux/devfs.c
@@ -1,0 +1,26 @@
+#include "devfs-posix.c"
+
+#include <linux/hdreg.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+
+static vlong
+osdisksize(int fd)
+{
+ uvlong u64;
+ long l;
+ struct hd_geometry geo;
+
+ memset(&geo, 0, sizeof geo);
+ l = 0;
+ u64 = 0;
+#ifdef BLKGETSIZE64
+ if(ioctl(fd, BLKGETSIZE64, &u64) >= 0)
+ return u64;
+#endif
+ if(ioctl(fd, BLKGETSIZE, &l) >= 0)
+ return l*512;
+ if(ioctl(fd, HDIO_GETGEO, &geo) >= 0)
+ return (vlong)geo.heads*geo.sectors*geo.cylinders*512;
+ return 0;
+}
--- /dev/null
+++ b/emu/Linux/emu
@@ -1,0 +1,105 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif6-posix ipaux
+ eia
+# audio audio-oss
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /dis
+# /n
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Linux/emu-g
@@ -1,0 +1,101 @@
+env
+ X11LIBS=
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ ip ipif6-posix ipaux
+ eia
+# audio audio
+ mem
+
+lib
+ interp
+ math
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ void setpointer(int x, int y){USED(x); USED(y);}
+ ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Linux/emu-wrt
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+# draw
+# pointer
+# snarf
+
+ ip ipif6-posix ipaux
+# eia
+# audio audio
+ mem
+
+lib
+ interp
+# tk
+# freetype
+ math
+# draw
+
+# memlayer
+# memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+# draw
+
+# tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+# freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ int dontcompile = 1;
+ unsigned long strtochan(char *s) {return 0;}
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Linux/mk-wrt
@@ -1,0 +1,8 @@
+#!/bin/sh
+
+OPENWRT=$HOME/OpenWrt-SDK-Linux-i686-1
+INFERNO=/usr/inferno
+
+PATH=$OPENWRT/staging_dir_mipsel/bin:$INFERNO/Linux/386/bin:$PATH
+
+mk OBJTYPE=spim CONF=emu-wrt CONFLIST=emu-wrt SYSLIBS=-lm WIN= $*
--- /dev/null
+++ b/emu/Linux/mkfile
@@ -1,0 +1,53 @@
+SYSTARG=Linux
+<../../mkconfig
+SYSTARG=Linux
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+X11LIBS= -lX11 -lXext # can remove or override using env section in config files
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+<mkfile-$OBJTYPE # sets $ARCHFILES
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ $ARCHFILES\
+ os.$O\
+ kproc-pthreads.$O\
+ segflush-$OBJTYPE.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+LIBNAMES=${LIBS:%=lib%.a}
+#libs=${LIBS:%=$ROOT/$OBJDIR/lib/lib%.a}
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= $X11LIBS -lm -lpthread
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+<../port/portmkfile
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/Linux/mkfile-arm
@@ -1,0 +1,2 @@
+ARCHFILES=\
+ arm-tas-v7.$O\
--- /dev/null
+++ b/emu/Linux/os.c
@@ -1,0 +1,304 @@
+#include <sys/types.h>
+#include <time.h>
+#include <termios.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+
+#include <stdint.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include <semaphore.h>
+
+#include <raise.h>
+
+/* glibc 2.3.3-NTPL messes up getpid() by trying to cache the result, so we'll do it ourselves */
+#include <sys/syscall.h>
+#define getpid() syscall(SYS_getpid)
+
+enum
+{
+ DELETE = 0x7f,
+ CTRLC = 'C'-'@',
+ NSTACKSPERALLOC = 16,
+ X11STACK= 256*1024
+};
+char *hosttype = "Linux";
+
+typedef sem_t Sem;
+
+extern int dflag;
+
+int gidnobody = -1;
+int uidnobody = -1;
+static struct termios tinit;
+
+static void
+sysfault(char *what, void *addr)
+{
+ char buf[64];
+
+ snprint(buf, sizeof(buf), "sys: %s%#p", what, addr);
+ disfault(nil, buf);
+}
+
+static void
+trapILL(int signo, siginfo_t *si, void *a)
+{
+ USED(signo);
+ USED(a);
+ sysfault("illegal instruction pc=", si->si_addr);
+}
+
+static int
+isnilref(siginfo_t *si)
+{
+ return si != 0 && (si->si_addr == (void*)~(uintptr_t)0 || (uintptr_t)si->si_addr < 512);
+}
+
+static void
+trapmemref(int signo, siginfo_t *si, void *a)
+{
+ USED(a); /* ucontext_t*, could fetch pc in machine-dependent way */
+ if(isnilref(si))
+ disfault(nil, exNilref);
+ else if(signo == SIGBUS)
+ sysfault("bad address addr=", si->si_addr); /* eg, misaligned */
+ else
+ sysfault("segmentation violation addr=", si->si_addr);
+}
+
+static void
+trapFPE(int signo, siginfo_t *si, void *a)
+{
+ char buf[64];
+
+ USED(signo);
+ USED(a);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux pc=%#p", getfsr(), si->si_addr);
+ disfault(nil, buf);
+}
+
+static void
+trapUSR1(int signo)
+{
+ int intwait;
+
+ USED(signo);
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+
+ if(intwait == 0) /* Not posted so it's a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ error("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+ struct sigaction act;
+ struct passwd *pw;
+ Proc *p;
+ char sys[64];
+
+ setsid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ pw = getpwnam("nobody");
+ if(pw != nil) {
+ uidnobody = pw->pw_uid;
+ gidnobody = pw->pw_gid;
+ }
+
+ if(dflag == 0)
+ termset();
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &act, nil);
+
+ /*
+ * For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGPIPE, SIG_IGN);
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+
+ if(sflag == 0) {
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_sigaction = trapFPE;
+ sigaction(SIGFPE, &act, nil);
+ act.sa_sigaction = trapmemref;
+ sigaction(SIGBUS, &act, nil);
+ sigaction(SIGSEGV, &act, nil);
+ act.sa_flags &= ~SA_SIGINFO;
+ }
+
+ p = newproc();
+ kprocinit(p);
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ p->env->uid = getuid();
+ p->env->gid = getgid();
+
+ emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ buf[0] = 'H' - '@';
+ break;
+ case CTRLC:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return 0;
+
+ if(sec0 == 0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec/1000;
+ time.tv_nsec= (milsec%1000)*1000000;
+ nanosleep(&time, NULL);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
--- /dev/null
+++ b/emu/Linux/segflush-386.c
@@ -1,0 +1,11 @@
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "dat.h"
+
+int
+segflush(void *a, ulong n)
+{
+ USED(a); USED(n);
+ return 0;
+}
--- /dev/null
+++ b/emu/Linux/segflush-arm.c
@@ -1,0 +1,14 @@
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "dat.h"
+
+#define SYS_cacheflush __ARM_NR_cacheflush
+
+int
+segflush(void *a, ulong n)
+{
+ if(n)
+ syscall(SYS_cacheflush, a, (char*)a+n-1, 1);
+ return 0;
+}
--- /dev/null
+++ b/emu/Linux/segflush-mips.S
@@ -1,0 +1,16 @@
+#include "syscall.h"
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+/*
+ * int segflush(void *p, ulong len)
+ */
+
+LEAF(segflush)
+ li a2,BCACHE
+ li v0,SYS_cacheflush
+ syscall
+ li v0,0
+ j $31
+ END(segflush)
--- /dev/null
+++ b/emu/Linux/segflush-power.c
@@ -1,0 +1,34 @@
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "dat.h"
+
+
+/*
+ * from geoff collyer's port
+ * invalidate instruction cache and write back data cache from a to a+n-1,
+ * at least.
+ */
+int
+segflush(void *a, ulong n)
+{
+ ulong *p;
+
+ // cache blocks are often eight words (32 bytes) long, sometimes 16 bytes.
+ // need to determine it dynamically?
+ for (p = (ulong *)((ulong)a & ~7UL); (char *)p < (char *)a + n; p++)
+ __asm__("dcbst 0,%0\n\t" // not dcbf, which writes back, then invalidates
+ "icbi 0,%0\n\t"
+ : // no output
+ : "ar" (p)
+ );
+ __asm__("sync\n\t"
+ : // no output
+ :
+ );
+ __asm__("isync\n\t"
+ : // no output
+ :
+ );
+ return 0;
+}
--- /dev/null
+++ b/emu/Linux/segflush-spim.S
@@ -1,0 +1,16 @@
+#include "syscall.h"
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+/*
+ * int segflush(void *p, ulong len)
+ */
+
+LEAF(segflush)
+ li a2,BCACHE
+ li v0,SYS_cacheflush
+ syscall
+ li v0,0
+ j $31
+ END(segflush)
--- /dev/null
+++ b/emu/MacOSX/NOTE
@@ -1,0 +1,4 @@
+- still interp mode only, jit not complete
+- mkfile uses Carbon only; mkfile-x11 uses X11R6 from Apple
+- both powerpc and x86 versions compiled from this source
+- mkfile-g doesn't include graphics (uses emu-g not emu as configuration)
--- /dev/null
+++ b/emu/MacOSX/NOTICE
@@ -1,0 +1,3 @@
+MacOSX port Copyright © 2001-2003 Corpus Callosum Corporation,
+with portions Copyright © 2001-2003 Geoff Collyer
+MacOSX-x86 Copyright © 2006 Corpus Callosum Corporation
--- /dev/null
+++ b/emu/MacOSX/asm-386.s
@@ -1,0 +1,28 @@
+/*
+ * these are the same as on the PC (eg, Linux)
+*/
+
+ .globl _FPsave
+_FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .globl _FPrestore
+_FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .globl __tas
+__tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/MacOSX/asm-power.s
@@ -1,0 +1,36 @@
+/*
+ * File: asm-power.s
+ *
+ * Copyright (c) 2003, Corpus Callosum Corporation. All rights reserved.
+ */
+
+#include <architecture/ppc/asm_help.h>
+
+.text
+
+LEAF(_FPsave)
+ mffs f0
+ stfd f0,0(r3)
+ blr
+END(_FPsave)
+
+LEAF(_FPrestore)
+ lfd f0,0(r3)
+ mtfsf 0xff,f0
+ blr
+END(_FPrestore)
+
+LEAF(__tas)
+ sync
+ mr r4,r3
+ addi r5,0,0x1
+1:
+ lwarx r3,0,r4
+ cmpwi r3,0x0
+ bne- 2f
+ stwcx. r5,0,r4
+ bne- 1b /* Lost reservation, try again */
+2:
+ sync
+ blr
+END(__tas)
--- /dev/null
+++ b/emu/MacOSX/cmd.c
@@ -1,0 +1,214 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgid(0, getpid());
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/MacOSX/deveia.c
@@ -1,0 +1,123 @@
+/*
+ * Darwin serial port definitions, uses IOKit to build sysdev
+ * Loosely based on FreeBSD/deveia.c
+ * Copyright © 1998, 1999 Lucent Technologies Inc. All rights reserved.
+ * Revisions Copyright © 1999, 2000 Vita Nuova Limited. All rights reserved.
+ * Revisions Copyright © 2003 Corpus Callosum Corporation. All rights reserved.
+*/
+
+#include <termios.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <mach/mach.h>
+
+#include <CoreFoundation/CFNumber.h>
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/serial/IOSerialKeys.h>
+#include <IOKit/IOBSD.h>
+
+#include <sys/ioctl.h>
+#include <sys/ttycom.h>
+
+#undef nil
+
+#define B14400 14400
+#define B28800 28800
+#define B57600 57600
+#define B76800 76800
+#define B115200 115200
+#define B230400 230400
+
+extern int vflag;
+
+#define MAXDEV 16
+static char *sysdev[MAXDEV];
+
+static void _buildsysdev(void);
+#define buildsysdev() _buildsysdev() /* for devfs-posix.c */
+
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {76800, B76800},
+ {115200, B115200},
+ {230400, B230400},
+ {0, -1}
+};
+
+static void
+_buildsysdev(void)
+{
+ mach_port_t port;
+ CFMutableDictionaryRef classesToMatch;
+ io_iterator_t serialPortIterator;
+ io_object_t serialDevice;
+ CFMutableArrayRef paths;
+ CFTypeRef path;
+ char eiapath[MAXPATHLEN];
+ CFIndex i, o, npath;
+
+ if(IOMasterPort(MACH_PORT_NULL, &port) != KERN_SUCCESS)
+ return;
+ classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
+ if(classesToMatch == NULL){
+ printf("IOServiceMatching returned a NULL dictionary.\n");
+ goto Failed;
+ }
+ CFDictionarySetValue(classesToMatch,
+ CFSTR(kIOSerialBSDTypeKey),
+ CFSTR(kIOSerialBSDAllTypes));
+
+ if(IOServiceGetMatchingServices(port, classesToMatch, &serialPortIterator) != KERN_SUCCESS)
+ goto Failed;
+
+ paths = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ while((serialDevice = IOIteratorNext(serialPortIterator)) != 0){
+ path = IORegistryEntryCreateCFProperty(serialDevice, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
+ if(path != NULL)
+ CFArrayAppendValue(paths, path);
+ IOObjectRelease(serialDevice);
+ }
+
+ npath = CFArrayGetCount(paths);
+ o = 0;
+ for(i = 0; i < npath && i < nelem(sysdev); i++){
+ if(CFStringGetCString(CFArrayGetValueAtIndex(paths, i), eiapath, sizeof(eiapath), kCFStringEncodingUTF8)){
+ sysdev[o] = strdup(eiapath);
+ if(vflag > 1)
+ print("BSD path: '%s'\n", sysdev[o]);
+ o++;
+ }
+ }
+
+ CFRelease(paths);
+ IOObjectRelease(serialPortIterator);
+
+Failed:
+ mach_port_deallocate(mach_task_self(), port);
+}
+
--- /dev/null
+++ b/emu/MacOSX/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/MacOSX/emu
@@ -1,0 +1,113 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+# audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ int dontcompile = 1;
+ int macjit = 1;
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/MacOSX/emu-g
@@ -1,0 +1,101 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ ip ipif-posix ipaux
+ eia
+# audio audio
+ mem
+
+lib
+ interp
+ math
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ int dontcompile = 1;
+ int macjit = 1;
+ void setpointer(int x, int y){USED(x); USED(y);}
+ ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/MacOSX/keycodes.h
@@ -1,0 +1,189 @@
+/* These are the Macintosh key scancode constants -- from Inside Macintosh */
+#define QZ_ESCAPE 0x35
+#define QZ_F1 0x7A
+#define QZ_F2 0x78
+#define QZ_F3 0x63
+#define QZ_F4 0x76
+#define QZ_F5 0x60
+#define QZ_F6 0x61
+#define QZ_F7 0x62
+#define QZ_F8 0x64
+#define QZ_F9 0x65
+#define QZ_F10 0x6D
+#define QZ_F11 0x67
+#define QZ_F12 0x6F
+#define QZ_PRINT 0x69
+#define QZ_SCROLLOCK 0x6B
+#define QZ_PAUSE 0x71
+#define QZ_POWER 0x7F
+#define QZ_BACKQUOTE 0x32
+#define QZ_1 0x12
+#define QZ_2 0x13
+#define QZ_3 0x14
+#define QZ_4 0x15
+#define QZ_5 0x17
+#define QZ_6 0x16
+#define QZ_7 0x1A
+#define QZ_8 0x1C
+#define QZ_9 0x19
+#define QZ_0 0x1D
+#define QZ_MINUS 0x1B
+#define QZ_EQUALS 0x18
+#define QZ_BACKSPACE 0x33
+#define QZ_INSERT 0x72
+#define QZ_HOME 0x73
+#define QZ_PAGEUP 0x74
+#define QZ_NUMLOCK 0x47
+#define QZ_KP_EQUALS 0x51
+#define QZ_KP_DIVIDE 0x4B
+#define QZ_KP_MULTIPLY 0x43
+#define QZ_TAB 0x30
+#define QZ_q 0x0C
+#define QZ_w 0x0D
+#define QZ_e 0x0E
+#define QZ_r 0x0F
+#define QZ_t 0x11
+#define QZ_y 0x10
+#define QZ_u 0x20
+#define QZ_i 0x22
+#define QZ_o 0x1F
+#define QZ_p 0x23
+#define QZ_LEFTBRACKET 0x21
+#define QZ_RIGHTBRACKET 0x1E
+#define QZ_BACKSLASH 0x2A
+#define QZ_DELETE 0x75
+#define QZ_END 0x77
+#define QZ_PAGEDOWN 0x79
+#define QZ_KP7 0x59
+#define QZ_KP8 0x5B
+#define QZ_KP9 0x5C
+#define QZ_KP_MINUS 0x4E
+#define QZ_CAPSLOCK 0x39
+#define QZ_a 0x00
+#define QZ_s 0x01
+#define QZ_d 0x02
+#define QZ_f 0x03
+#define QZ_g 0x05
+#define QZ_h 0x04
+#define QZ_j 0x26
+#define QZ_k 0x28
+#define QZ_l 0x25
+#define QZ_SEMICOLON 0x29
+#define QZ_QUOTE 0x27
+#define QZ_RETURN 0x24
+#define QZ_KP4 0x56
+#define QZ_KP5 0x57
+#define QZ_KP6 0x58
+#define QZ_KP_PLUS 0x45
+#define QZ_LSHIFT 0x38
+#define QZ_z 0x06
+#define QZ_x 0x07
+#define QZ_c 0x08
+#define QZ_v 0x09
+#define QZ_b 0x0B
+#define QZ_n 0x2D
+#define QZ_m 0x2E
+#define QZ_COMMA 0x2B
+#define QZ_PERIOD 0x2F
+#define QZ_SLASH 0x2C
+/* These are the same as the left versions - use left by default */
+#if 0
+#define QZ_RSHIFT 0x38
+#endif
+#define QZ_UP 0x7E
+#define QZ_KP1 0x53
+#define QZ_KP2 0x54
+#define QZ_KP3 0x55
+#define QZ_KP_ENTER 0x4C
+#define QZ_LCTRL 0x3B
+#define QZ_LALT 0x3A
+#define QZ_LMETA 0x37
+#define QZ_SPACE 0x31
+/* These are the same as the left versions - use left by default */
+#if 0
+#define QZ_RMETA 0x37
+#define QZ_RALT 0x3A
+#define QZ_RCTRL 0x3B
+#endif
+#define QZ_LEFT 0x7B
+#define QZ_DOWN 0x7D
+#define QZ_RIGHT 0x7C
+#define QZ_KP0 0x52
+#define QZ_KP_PERIOD 0x41
+
+/* Wierd, these keys are on my iBook under MacOS X */
+#define QZ_IBOOK_ENTER 0x34
+#define QZ_IBOOK_LEFT 0x3B
+#define QZ_IBOOK_RIGHT 0x3C
+#define QZ_IBOOK_DOWN 0x3D
+#define QZ_IBOOK_UP 0x3E
+#define KEY_ENTER 13
+#define KEY_TAB 9
+
+#define KEY_BASE 0x100
+
+/* Function keys */
+#define KEY_F (KEY_BASE+64)
+
+/* Control keys */
+#define KEY_CTRL (KEY_BASE)
+#define KEY_BACKSPACE (KEY_CTRL+0)
+#define KEY_DELETE (KEY_CTRL+1)
+#define KEY_INSERT (KEY_CTRL+2)
+#define KEY_HOME (KEY_CTRL+3)
+#define KEY_END (KEY_CTRL+4)
+#define KEY_PAGE_UP (KEY_CTRL+5)
+#define KEY_PAGE_DOWN (KEY_CTRL+6)
+#define KEY_ESC (KEY_CTRL+7)
+
+/* Control keys short name */
+#define KEY_BS KEY_BACKSPACE
+#define KEY_DEL KEY_DELETE
+#define KEY_INS KEY_INSERT
+#define KEY_PGUP KEY_PAGE_UP
+#define KEY_PGDOWN KEY_PAGE_DOWN
+#define KEY_PGDWN KEY_PAGE_DOWN
+
+/* Cursor movement */
+#define KEY_CRSR (KEY_BASE+16)
+#define KEY_RIGHT (KEY_CRSR+0)
+#define KEY_LEFT (KEY_CRSR+1)
+#define KEY_DOWN (KEY_CRSR+2)
+#define KEY_UP (KEY_CRSR+3)
+
+/* Multimedia keyboard/remote keys */
+#define KEY_MM_BASE (0x100+384)
+#define KEY_POWER (KEY_MM_BASE+0)
+#define KEY_MENU (KEY_MM_BASE+1)
+#define KEY_PLAY (KEY_MM_BASE+2)
+#define KEY_PAUSE (KEY_MM_BASE+3)
+#define KEY_PLAYPAUSE (KEY_MM_BASE+4)
+#define KEY_STOP (KEY_MM_BASE+5)
+#define KEY_FORWARD (KEY_MM_BASE+6)
+#define KEY_REWIND (KEY_MM_BASE+7)
+#define KEY_NEXT (KEY_MM_BASE+8)
+#define KEY_PREV (KEY_MM_BASE+9)
+#define KEY_VOLUME_UP (KEY_MM_BASE+10)
+#define KEY_VOLUME_DOWN (KEY_MM_BASE+11)
+#define KEY_MUTE (KEY_MM_BASE+12)
+
+/* Keypad keys */
+#define KEY_KEYPAD (KEY_BASE+32)
+#define KEY_KP0 (KEY_KEYPAD+0)
+#define KEY_KP1 (KEY_KEYPAD+1)
+#define KEY_KP2 (KEY_KEYPAD+2)
+#define KEY_KP3 (KEY_KEYPAD+3)
+#define KEY_KP4 (KEY_KEYPAD+4)
+#define KEY_KP5 (KEY_KEYPAD+5)
+#define KEY_KP6 (KEY_KEYPAD+6)
+#define KEY_KP7 (KEY_KEYPAD+7)
+#define KEY_KP8 (KEY_KEYPAD+8)
+#define KEY_KP9 (KEY_KEYPAD+9)
+#define KEY_KPDEC (KEY_KEYPAD+10)
+#define KEY_KPINS (KEY_KEYPAD+11)
+#define KEY_KPDEL (KEY_KEYPAD+12)
+#define KEY_KPENTER (KEY_KEYPAD+13)
+
+/* Special keys */
+#define KEY_INTERN (0x1000)
+#define KEY_CLOSE_WIN (KEY_INTERN+0)
--- /dev/null
+++ b/emu/MacOSX/mkfile
@@ -1,0 +1,56 @@
+SYSTARG=MacOSX
+<../../mkconfig
+SYSTARG=MacOSX
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"'\
+ '-DOBJTYPE="'$OBJTYPE'"'\
+ -DEMU -I. -I../port\
+ -I$ROOT/$SYSTARG/$OBJTYPE/include\
+ -I$ROOT/include -I$ROOT/libinterp\
+ $CTHREADFLAGS $CFLAGS $EMUOPTIONS\
+
+KERNDATE=`{$NDATE}
+
+SYSLIBS= \
+ -lm\
+ -framework Carbon -framework QuickTime\
+ -lpthread\
+ -framework CoreFoundation\
+ -framework IOKit\
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.c:N: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/MacOSX/mkfile-g
@@ -1,0 +1,55 @@
+SYSTARG=MacOSX
+<../../mkconfig
+SYSTARG=MacOSX
+
+#Configurable parameters
+
+CONF=emu-g #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"'\
+ '-DOBJTYPE="'$OBJTYPE'"'\
+ -DEMU -I. -I../port\
+ -I$ROOT/$SYSTARG/$OBJTYPE/include\
+ -I$ROOT/include -I$ROOT/libinterp\
+ $CTHREADFLAGS $CFLAGS $EMUOPTIONS\
+
+KERNDATE=`{$NDATE}
+
+SYSLIBS= \
+ -lm\
+ -lpthread\
+ -framework CoreFoundation\
+ -framework IOKit\
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.c:N: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/MacOSX/mkfile-x11
@@ -1,0 +1,59 @@
+SYSTARG=MacOSX
+<../../mkconfig
+SYSTARG=MacOSX
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ win-x11a.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"'\
+ '-DOBJTYPE="'$OBJTYPE'"'\
+ -DEMU -I. -I../port\
+ -I$ROOT/$SYSTARG/$OBJTYPE/include\
+ -I$ROOT/include -I$ROOT/libinterp\
+ $CTHREADFLAGS $CFLAGS $EMUOPTIONS\
+ -I/usr/X11R6/include
+
+KERNDATE=`{$NDATE}
+
+LDFLAGS=$LDFLAGS -L/usr/X11R6/lib
+
+SYSLIBS= \
+ -lm -lX11 -lXext\
+ -lpthread\
+ -framework CoreFoundation\
+ -framework IOKit\
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.c:N: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/MacOSX/os.c
@@ -1,0 +1,574 @@
+/*
+ * Loosely based on FreeBSD/os.c and Solaris/os.c
+ * Copyright © 1998, 1999 Lucent Technologies Inc. All rights reserved.
+ * Revisions Copyright © 1999, 2000 Vita Nuova Limited. All rights reserved.
+ * Revisions Copyright © 2002, 2003 Corpus Callosum Corporation. All rights reserved.
+ */
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include <raise.h>
+
+#undef _POSIX_C_SOURCE
+#undef getwd
+
+#include <unistd.h>
+#include <pthread.h>
+#include <time.h>
+#include <termios.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <sched.h>
+#include <errno.h>
+#include <sys/ucontext.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <mach/mach_init.h>
+#include <mach/task.h>
+#include <mach/vm_map.h>
+
+#if defined(__ppc__)
+#include <architecture/ppc/cframe.h>
+#endif
+
+enum
+{
+ DELETE = 0x7F
+};
+char *hosttype = "MacOSX";
+char *cputype = OBJTYPE;
+
+typedef struct Sem Sem;
+struct Sem {
+ pthread_cond_t c;
+ pthread_mutex_t m;
+ int v;
+};
+
+static pthread_key_t prdakey;
+
+extern int dflag;
+
+Proc*
+getup(void)
+{
+ return pthread_getspecific(prdakey);
+}
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+ Proc *p;
+ Sem *sem;
+
+ USED(t);
+ USED(msg);
+
+ lock(&procs.l);
+ p = up;
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(p->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ if(0)
+ print("pexit: %s: %s\n", p->text, msg);
+
+ e = p->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ free(e->user);
+ }
+ free(p->prog);
+ sem = p->os;
+ if(sem != nil){
+ pthread_cond_destroy(&sem->c);
+ pthread_mutex_destroy(&sem->m);
+ }
+ free(p->os);
+ free(p);
+ pthread_exit(0);
+}
+
+
+
+static void
+sysfault(char *what, void *addr)
+{
+ char buf[64];
+
+ snprint(buf, sizeof(buf), "sys: %s%#p", what, addr);
+ disfault(nil, buf);
+}
+
+static void
+trapILL(int signo, siginfo_t *si, void *a)
+{
+ USED(signo);
+ USED(a);
+ sysfault("illegal instruction pc=", si->si_addr);
+}
+
+static int
+isnilref(siginfo_t *si)
+{
+ return si != 0 && (si->si_addr == (void*)~(uintptr_t)0 || (uintptr_t)si->si_addr < 512);
+}
+
+static void
+trapmemref(int signo, siginfo_t *si, void *a)
+{
+ USED(a); /* ucontext_t*, could fetch pc in machine-dependent way */
+ if(isnilref(si))
+ disfault(nil, exNilref);
+ else if(signo == SIGBUS)
+ sysfault("bad address addr=", si->si_addr); /* eg, misaligned */
+ else
+ sysfault("segmentation violation addr=", si->si_addr);
+}
+
+static void
+trapFPE(int signo, siginfo_t *si, void *a)
+{
+ char buf[64];
+
+ USED(signo);
+ USED(a);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux pc=%#p", getfsr(), si->si_addr);
+ disfault(nil, buf);
+}
+
+void
+trapUSR1(int signo)
+{
+ USED(signo);
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+ if(up->intwait == 0) /* Not posted so its a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+
+ up->intwait = 0; /* Clear it so the proc can continue */
+}
+
+/* from geoff collyer's port */
+void
+printILL(int sig, siginfo_t *si, void *v)
+{
+ USED(sig);
+ USED(v);
+ panic("illegal instruction with code=%d at address=%p, opcode=%#x\n",
+ si->si_code, si->si_addr, *(uchar*)si->si_addr);
+}
+
+static void
+setsigs(void)
+{
+ struct sigaction act;
+
+ memset(&act, 0 , sizeof(act));
+
+ /*
+ * For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGPIPE, SIG_IGN);
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+
+ act.sa_handler = trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+
+ if(sflag == 0) {
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_sigaction = trapFPE;
+ sigaction(SIGFPE, &act, nil);
+ act.sa_sigaction = trapmemref;
+ sigaction(SIGBUS, &act, nil);
+ sigaction(SIGSEGV, &act, nil);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+ } else {
+ act.sa_sigaction = printILL;
+ act.sa_flags = SA_SIGINFO;
+ sigaction(SIGILL, &act, nil);
+ }
+}
+
+
+
+
+void *
+tramp(void *arg)
+{
+ Proc *p = arg;
+ p->sigid = (int)pthread_self();
+ if(pthread_setspecific(prdakey, arg)) {
+ print("set specific data failed in tramp\n");
+ pthread_exit(0);
+ }
+ p->func(p->arg);
+ pexit("{Tramp}", 0);
+ return NULL;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ pthread_t thread;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ pthread_attr_t attr;
+ Sem *sem;
+
+ p = newproc();
+ if(p == nil)
+ panic("kproc: no memory");
+ sem = malloc(sizeof(*sem));
+ if(sem == nil)
+ panic("can't allocate semaphore");
+ pthread_cond_init(&sem->c, NULL);
+ pthread_mutex_init(&sem->m, NULL);
+ sem->v = 0;
+ p->os = sem;
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ } else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if(pthread_attr_init(&attr) == -1)
+ panic("pthread_attr_init failed");
+
+ pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
+ pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ if(pthread_create(&thread, &attr, tramp, p))
+ panic("thr_create failed\n");
+ pthread_attr_destroy(&attr);
+}
+
+int
+segflush(void *va, ulong len)
+{
+ kern_return_t err;
+ vm_machine_attribute_val_t value = MATTR_VAL_ICACHE_FLUSH;
+
+ err = vm_machine_attribute( (vm_map_t)mach_task_self(),
+ (vm_address_t)va,
+ (vm_size_t)len,
+ MATTR_CACHE,
+ &value);
+ if(err != KERN_SUCCESS)
+ print("segflush: failure (%d) address %lud\n", err, va);
+ return (int)err;
+}
+
+void
+oshostintr(Proc *p)
+{
+ pthread_kill((pthread_t)p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ Sem *sem;
+
+ sem = up->os;
+ pthread_mutex_lock(&sem->m);
+ while(sem->v == 0)
+ pthread_cond_wait(&sem->c, &sem->m);
+ sem->v--;
+ pthread_mutex_unlock(&sem->m);
+}
+
+void
+osready(Proc *p)
+{
+ Sem *sem;
+
+ sem = p->os;
+ pthread_mutex_lock(&sem->m);
+ sem->v++;
+ pthread_cond_signal(&sem->c);
+ pthread_mutex_unlock(&sem->m);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON | ECHO | ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ exit(0);
+}
+
+
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody()
+{
+ struct passwd *pwd;
+
+ if((pwd = getpwnam("nobody"))) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+libinit(char *imod)
+{
+ struct passwd *pw;
+ Proc *p;
+ char sys[64];
+
+ setsid();
+
+ // setup personality
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ getnobody();
+
+ if(dflag == 0)
+ termset();
+
+ setsigs();
+
+ if(pthread_key_create(&prdakey, NULL))
+ print("key_create failed\n");
+
+ p = newproc();
+ if(pthread_setspecific(prdakey, p))
+ panic("set specific thread data failed\n");
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ up->env->uid = getuid();
+ up->env->gid = getgid();
+
+ emuinit(imod);
+}
+
+
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t, NULL) < 0)
+ return(0);
+ if(sec0 == 0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return((t.tv_sec - sec0) * 1000 + (t.tv_usec - usec0 + 500) / 1000);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+ time.tv_sec = milsec / 1000;
+ time.tv_nsec = (milsec % 1000) * 1000000;
+ nanosleep(&time, nil);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ pthread_yield_np();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+// pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param);
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+__typeof__(sbrk(0))
+sbrk(int size)
+{
+ void *brk;
+ kern_return_t err;
+
+ err = vm_allocate( (vm_map_t) mach_task_self(),
+ (vm_address_t *)&brk,
+ size,
+ VM_FLAGS_ANYWHERE);
+ if(err != KERN_SUCCESS)
+ brk = (void*)-1;
+ return brk;
+}
--- /dev/null
+++ b/emu/MacOSX/win.c
@@ -1,0 +1,735 @@
+// in this file, _Rect is os x Rect,
+// _Point is os x Point
+#define Point _Point
+#define Rect _Rect
+
+#include <Carbon/Carbon.h>
+//#include <QuickTime/QuickTime.h> // for full screen
+
+#undef Rect
+#undef Point
+
+#undef nil
+
+#include "dat.h"
+#include "fns.h"
+#undef log2
+#include <draw.h>
+#include <memdraw.h>
+#include "cursor.h"
+#include "keyboard.h"
+#include "keycodes.h"
+
+#define Kup Up
+#define Kleft Left
+#define Kdown Down
+#define Kright Right
+#define Kalt LAlt
+#define Kctl LCtrl
+#define Kshift LShift
+#define Kpgup Pgup
+#define Kpgdown Pgdown
+#define Khome Home
+#define Kins Ins
+#define Kend End
+
+#define rWindowResource 128
+
+extern void flushmemscreen(Rectangle);
+
+Memimage *gscreen;
+
+static int readybit;
+static Rendez rend;
+static int triedscreen;
+
+///
+// menu
+//
+static MenuRef windMenu;
+static MenuRef viewMenu;
+
+enum {
+ kQuitCmd = 1,
+ kFullScreenCmd = 2,
+};
+
+static WindowGroupRef winGroup = NULL;
+static WindowRef theWindow = NULL;
+static CGContextRef context;
+static CGDataProviderRef dataProviderRef;
+static CGImageRef fullScreenImage;
+static CGRect devRect;
+static CGRect bounds;
+static PasteboardRef appleclip;
+static _Rect winRect;
+
+static Boolean altPressed = false;
+static Boolean button2 = false;
+static Boolean button3 = false;
+
+static Boolean needflush = false;
+
+
+static int
+isready(void*a)
+{
+ return readybit;
+}
+
+CGContextRef QuartzContext;
+
+static OSStatus MainWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
+static OSStatus MainWindowCommandHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
+
+static void winproc(void *a);
+static void flushproc(void *a);
+
+void
+screeninit(void)
+{
+ int fmt;
+ int dx, dy;
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ SetFrontProcess(&psn);
+
+ fmt = XBGR32; //XRGB32;
+
+ devRect = CGDisplayBounds(CGMainDisplayID());
+// devRect.origin.x = 0;
+// devRect.origin.y = 0;
+// devRect.size.width = 1024;
+// devRect.size.height = 768;
+ dx = devRect.size.width;
+ dy = devRect.size.height;
+
+ if(1){ /* TO DO: new dev draw for changing screen size */
+ dx = Xsize;
+ dy = Ysize;
+ }
+
+ gscreen = allocmemimage(Rect(0,0,dx,dy), fmt);
+ dataProviderRef = CGDataProviderCreateWithData(0, gscreen->data->bdata,
+ dx * dy * 4, 0);
+ fullScreenImage = CGImageCreate(dx, dy, 8, 32, dx * 4,
+ CGColorSpaceCreateDeviceRGB(),
+ kCGImageAlphaNoneSkipLast,
+ dataProviderRef, 0, 0, kCGRenderingIntentDefault);
+
+ kproc("osxscreen", winproc, nil, 0);
+ kproc("osxflush", flushproc, nil, 0);
+ Sleep(&rend, isready, nil);
+}
+
+void
+window_resized(void)
+{
+ GetWindowBounds(theWindow, kWindowContentRgn, &winRect);
+
+ bounds = CGRectMake(0, 0, winRect.right-winRect.left, winRect.bottom - winRect.top);
+}
+
+static void
+flushproc(void *a)
+{
+ for(;;) {
+ if(needflush) {
+ drawqlock();
+ needflush = false;
+ QDBeginCGContext(GetWindowPort(theWindow), &context);
+ CGContextFlush(context);
+ QDEndCGContext(GetWindowPort(theWindow), &context);
+ drawqunlock();
+ }
+ usleep(33333);
+ }
+}
+
+static void
+winproc(void *a)
+{
+ MenuItemIndex index;
+ int dx, dy;
+
+ winRect.left = 30;
+ winRect.top = 60;
+ dx = devRect.size.width*0.75; /* devRect is full screen; take only most of it */
+ dy = devRect.size.height*0.75;
+ if(1){ /* TO DO */
+ dx = Xsize;
+ dy = Ysize;
+ }
+ winRect.bottom = winRect.top + dy;
+ winRect.right = winRect.left + dx;
+
+ ClearMenuBar();
+ InitCursor();
+
+ CreateStandardWindowMenu(0, &windMenu);
+ InsertMenu(windMenu, 0);
+
+ CreateNewMenu(1004, 0, &viewMenu);
+ SetMenuTitleWithCFString(viewMenu, CFSTR("View"));
+ AppendMenuItemTextWithCFString(viewMenu, CFSTR("Full Screen"), 0,
+ kFullScreenCmd, &index);
+ SetMenuItemCommandKey(viewMenu, index, 0, 'F');
+ AppendMenuItemTextWithCFString(viewMenu, CFSTR("ctrl-opt to return"),
+ kMenuItemAttrDisabled,
+ kFullScreenCmd, &index);
+ InsertMenu(viewMenu, GetMenuID(windMenu));
+
+ DrawMenuBar();
+ uint32_t windowAttrs = 0
+ | kWindowCloseBoxAttribute
+ | kWindowCollapseBoxAttribute
+// | kWindowResizableAttribute // TO DO
+ | kWindowStandardHandlerAttribute
+// | kWindowFullZoomAttribute // TO DO
+ ;
+
+ CreateNewWindow(kDocumentWindowClass, windowAttrs, &winRect, &theWindow);
+ CreateWindowGroup(0, &winGroup);
+ SetWindowGroup(theWindow, winGroup);
+
+ SetWindowTitleWithCFString(theWindow, CFSTR("Inferno"));
+
+ if(PasteboardCreate(kPasteboardClipboard, &appleclip) != noErr)
+ sysfatal("pasteboard create failed");
+
+ const EventTypeSpec commands[] = {
+ { kEventClassWindow, kEventWindowClosed },
+ { kEventClassWindow, kEventWindowBoundsChanged },
+ { kEventClassCommand, kEventCommandProcess }
+ };
+ const EventTypeSpec events[] = {
+ { kEventClassKeyboard, kEventRawKeyDown },
+ { kEventClassKeyboard, kEventRawKeyModifiersChanged },
+ { kEventClassKeyboard, kEventRawKeyRepeat },
+ { kEventClassMouse, kEventMouseDown },
+ { kEventClassMouse, kEventMouseUp },
+ { kEventClassMouse, kEventMouseMoved },
+ { kEventClassMouse, kEventMouseDragged },
+ { kEventClassMouse, kEventMouseWheelMoved },
+ };
+
+ InstallApplicationEventHandler (
+ NewEventHandlerUPP (MainWindowEventHandler),
+ GetEventTypeCount(events),
+ events,
+ NULL,
+ NULL);
+ InstallWindowEventHandler (
+ theWindow,
+ NewEventHandlerUPP (MainWindowCommandHandler),
+ GetEventTypeCount(commands),
+ commands,
+ theWindow,
+ NULL);
+
+ ShowWindow(theWindow);
+ ShowMenuBar();
+ window_resized();
+ SelectWindow(theWindow);
+ // Run the event loop
+ readybit = 1;
+ Wakeup(&rend);
+ RunApplicationEventLoop();
+
+}
+
+static int
+convert_key(UInt32 key, UInt32 charcode)
+{
+ switch(key) {
+ case QZ_IBOOK_ENTER:
+ case QZ_RETURN: return '\n';
+ case QZ_ESCAPE: return 27;
+ case QZ_BACKSPACE: return '\b';
+ case QZ_LALT: return Kalt;
+ case QZ_LCTRL: return Kctl;
+ case QZ_LSHIFT: return Kshift;
+ case QZ_F1: return KF+1;
+ case QZ_F2: return KF+2;
+ case QZ_F3: return KF+3;
+ case QZ_F4: return KF+4;
+ case QZ_F5: return KF+5;
+ case QZ_F6: return KF+6;
+ case QZ_F7: return KF+7;
+ case QZ_F8: return KF+8;
+ case QZ_F9: return KF+9;
+ case QZ_F10: return KF+10;
+ case QZ_F11: return KF+11;
+ case QZ_F12: return KF+12;
+ case QZ_INSERT: return Kins;
+ case QZ_DELETE: return 0x7F;
+ case QZ_HOME: return Khome;
+ case QZ_END: return Kend;
+ case QZ_KP_PLUS: return '+';
+ case QZ_KP_MINUS: return '-';
+ case QZ_TAB: return '\t';
+ case QZ_PAGEUP: return Kpgup;
+ case QZ_PAGEDOWN: return Kpgdown;
+ case QZ_UP: return Kup;
+ case QZ_DOWN: return Kdown;
+ case QZ_LEFT: return Kleft;
+ case QZ_RIGHT: return Kright;
+ case QZ_KP_MULTIPLY: return '*';
+ case QZ_KP_DIVIDE: return '/';
+ case QZ_KP_ENTER: return '\n';
+ case QZ_KP_PERIOD: return '.';
+ case QZ_KP0: return '0';
+ case QZ_KP1: return '1';
+ case QZ_KP2: return '2';
+ case QZ_KP3: return '3';
+ case QZ_KP4: return '4';
+ case QZ_KP5: return '5';
+ case QZ_KP6: return '6';
+ case QZ_KP7: return '7';
+ case QZ_KP8: return '8';
+ case QZ_KP9: return '9';
+ default: return charcode;
+ }
+}
+
+void
+sendbuttons(int b, int x, int y)
+{
+ mousetrack(b, x, y, 0);
+}
+
+static Ptr fullScreenRestore;
+static int amFullScreen = 0;
+static WindowRef oldWindow = NULL;
+
+static void
+leave_full_screen(void)
+{
+ if(amFullScreen){
+ EndFullScreen(fullScreenRestore, 0);
+ theWindow = oldWindow;
+ ShowWindow(theWindow);
+ amFullScreen = 0;
+ window_resized();
+ Rectangle rect = { { 0, 0 }, { bounds.size.width, bounds.size.height} };
+ drawqlock();
+ flushmemscreen(rect);
+ drawqunlock();
+ }
+}
+
+static void
+full_screen(void)
+{
+ if(!amFullScreen){
+ oldWindow = theWindow;
+ HideWindow(theWindow);
+ BeginFullScreen(&fullScreenRestore, 0, 0, 0, &theWindow, 0, 0);
+ amFullScreen = 1;
+ window_resized();
+ Rectangle rect = { { 0, 0 },
+ { bounds.size.width,
+ bounds.size.height} };
+ drawqlock();
+ flushmemscreen(rect);
+ drawqunlock();
+ }
+}
+
+static OSStatus
+MainWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
+{
+ OSStatus result = noErr;
+ result = CallNextEventHandler(nextHandler, event);
+ UInt32 class = GetEventClass (event);
+ UInt32 kind = GetEventKind (event);
+ static uint32_t mousebuttons = 0; // bitmask of buttons currently down
+ static uint32_t mouseX = 0;
+ static uint32_t mouseY = 0;
+
+ if(class == kEventClassKeyboard) {
+ char macCharCodes;
+ UInt32 macKeyCode;
+ UInt32 macKeyModifiers;
+
+ GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar,
+ NULL, sizeof(macCharCodes), NULL, &macCharCodes);
+ GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL,
+ sizeof(macKeyCode), NULL, &macKeyCode);
+ GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL,
+ sizeof(macKeyModifiers), NULL, &macKeyModifiers);
+ switch(kind) {
+ case kEventRawKeyModifiersChanged:
+ if (macKeyModifiers == (controlKey | optionKey)) leave_full_screen();
+
+ switch(macKeyModifiers & (optionKey | cmdKey)) {
+ case (optionKey | cmdKey):
+ /* due to chording we need to handle the case when both
+ * modifier keys are pressed at the same time.
+ * currently it's only 2-3 snarf and the 3-2 noop
+ */
+ altPressed = true;
+ if(mousebuttons & 1 || mousebuttons & 2 || mousebuttons & 4) {
+ mousebuttons |= 2; /* set button 2 */
+ mousebuttons |= 4; /* set button 3 */
+ button2 = true;
+ button3 = true;
+ sendbuttons(mousebuttons, mouseX, mouseY);
+ }
+ break;
+ case optionKey:
+ altPressed = true;
+ if(mousebuttons & 1 || mousebuttons & 4) {
+ mousebuttons |= 2; /* set button 2 */
+ button2 = true;
+ sendbuttons(mousebuttons, mouseX, mouseY);
+ }
+ break;
+ case cmdKey:
+ if(mousebuttons & 1 || mousebuttons & 2) {
+ mousebuttons |= 4; /* set button 3 */
+ button3 = true;
+ sendbuttons(mousebuttons, mouseX, mouseY);
+ }else
+ gkbdputc(gkbdq, Latin);
+ break;
+ case 0:
+ default:
+ if(button2 || button3) {
+ if(button2) {
+ mousebuttons &= ~2; /* clear button 2 */
+ button2 = false;
+ altPressed = false;
+ }
+ if(button3) {
+ mousebuttons &= ~4; /* clear button 3 */
+ button3 = false;
+ }
+ sendbuttons(mousebuttons, mouseX, mouseY);
+ }
+ if(altPressed) {
+ gkbdputc(gkbdq, Kalt);
+ altPressed = false;
+ }
+ break;
+ }
+ break;
+ case kEventRawKeyDown:
+ case kEventRawKeyRepeat:
+ if(macKeyModifiers != cmdKey) {
+ int key;
+ key = convert_key(macKeyCode, macCharCodes);
+ if(key != -1)
+ gkbdputc(gkbdq, key);
+ }else
+ result = eventNotHandledErr;
+ break;
+ default:
+ break;
+ }
+ }
+ else if(class == kEventClassMouse) {
+ _Point mousePos;
+
+ GetEventParameter(event, kEventParamMouseLocation, typeQDPoint,
+ 0, sizeof mousePos, 0, &mousePos);
+
+ switch(kind) {
+ case kEventMouseWheelMoved:
+ {
+ int32_t wheeldelta;
+ GetEventParameter(event,kEventParamMouseWheelDelta,typeSInt32,
+ 0,sizeof(wheeldelta), 0, &wheeldelta);
+ mouseX = mousePos.h - winRect.left;
+ mouseY = mousePos.v - winRect.top;
+ sendbuttons(wheeldelta>0 ? 8 : 16, mouseX, mouseY);
+ break;
+ }
+ case kEventMouseUp:
+ case kEventMouseDown:
+ {
+ uint32_t buttons;
+ uint32_t modifiers;
+ uint32_t clkcnt;
+
+ GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
+ 0, sizeof(modifiers), 0, &modifiers);
+ GetEventParameter(event, kEventParamMouseChord, typeUInt32,
+ 0, sizeof buttons, 0, &buttons);
+ GetEventParameter(event, kEventParamClickCount, typeUInt32,
+ 0, sizeof(clkcnt), 0, &clkcnt);
+
+ /* simulate other buttons via alt/apple key. like x11 */
+ if(modifiers & optionKey) {
+ mousebuttons = ((buttons & 1) ? 2 : 0);
+ altPressed = false;
+ } else if(modifiers & cmdKey)
+ mousebuttons = ((buttons & 1) ? 4 : 0);
+ else
+ mousebuttons = (buttons & 1);
+
+ mousebuttons |= ((buttons & 2)<<1);
+ mousebuttons |= ((buttons & 4)>>1);
+ if(clkcnt > 1)
+ mousebuttons |= 1<<8;
+
+ } /* Fallthrough */
+ case kEventMouseMoved:
+ case kEventMouseDragged:
+ mouseX = mousePos.h - winRect.left;
+ mouseY = mousePos.v - winRect.top;
+ sendbuttons(mousebuttons, mouseX, mouseY);
+ break;
+ default:
+ result = eventNotHandledErr;
+ break;
+ }
+ }
+ return result;
+}
+
+
+//default window command handler (from menus)
+static OSStatus
+MainWindowCommandHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
+{
+ OSStatus result = noErr;
+ UInt32 class = GetEventClass (event);
+ UInt32 kind = GetEventKind (event);
+
+ result = CallNextEventHandler(nextHandler, event);
+
+ if(class == kEventClassCommand) {
+ HICommand theHICommand;
+ GetEventParameter(event, kEventParamDirectObject, typeHICommand,
+ NULL, sizeof(HICommand), NULL, &theHICommand);
+
+ switch(theHICommand.commandID) {
+ case kHICommandQuit:
+ cleanexit(0);
+ break;
+
+ case kFullScreenCmd:
+ full_screen();
+ break;
+
+ default:
+ result = eventNotHandledErr;
+ break;
+ }
+ } else if(class == kEventClassWindow) {
+ WindowRef window;
+ _Rect rectPort = {0,0,0,0};
+
+ GetEventParameter(event, kEventParamDirectObject, typeWindowRef,
+ NULL, sizeof(WindowRef), NULL, &window);
+
+ if(window)
+ GetPortBounds(GetWindowPort(window), &rectPort);
+
+ switch(kind) {
+ case kEventWindowClosed:
+ theWindow = NULL;
+ cleanexit(0); // only one window
+ break;
+
+ //resize window
+ case kEventWindowBoundsChanged:
+ window_resized();
+ Rectangle rect = { { 0, 0 },
+ { bounds.size.width,
+ bounds.size.height} };
+ drawqlock();
+ flushmemscreen(rect);
+ drawqunlock();
+ break;
+
+ default:
+ result = eventNotHandledErr;
+ break;
+ }
+ }
+
+ return result;
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ CGRect rbounds;
+
+ // sanity check. Trips from the initial "terminal"
+ if (r.max.x < r.min.x || r.max.y < r.min.y)
+ return;
+
+ rbounds.size.width = r.max.x - r.min.x;
+ rbounds.size.height = r.max.y - r.min.y;
+ rbounds.origin.x = r.min.x;
+ rbounds.origin.y = r.min.y;
+
+ if(rbounds.size.width <= 0 || rbounds.size.height <= 0)
+ return;
+
+ QDBeginCGContext(GetWindowPort(theWindow), &context);
+
+ // The sub-image is relative to our whole screen image.
+ CGImageRef subimg = CGImageCreateWithImageInRect(fullScreenImage, rbounds);
+
+ // Drawing the sub-image is relative to the window.
+ rbounds.origin.y = winRect.bottom - winRect.top - r.min.y - rbounds.size.height;
+ CGContextDrawImage(context, rbounds, subimg);
+ CGImageRelease(subimg);
+ QDEndCGContext(GetWindowPort(theWindow), &context);
+
+ needflush = true;
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen)
+{
+ if(!triedscreen) {
+ triedscreen = 1;
+ screeninit(); /* TO DO: call this elsewhere? */
+ }
+ *r = gscreen->r;
+ *chan = gscreen->chan;
+ *depth = gscreen->depth;
+ *width = gscreen->width;
+ *softscreen = 1;
+
+ return gscreen->data->bdata;
+}
+
+// PAL - no palette handling. Don't intend to either.
+void
+getcolor(ulong i, ulong *r, ulong *g, ulong *b)
+{
+
+// PAL: Certainly wrong to return a grayscale.
+ *r = i;
+ *g = i;
+ *b = i;
+}
+
+void
+setcolor(ulong index, ulong r, ulong g, ulong b)
+{
+ USED(index); USED(r); USED(g); USED(b);
+}
+
+enum{
+ SnarfSize= 100*1024
+};
+
+static char snarf[3*SnarfSize+1];
+static Rune rsnarf[SnarfSize+1];
+
+char*
+clipread(void)
+{
+ CFDataRef cfdata;
+ OSStatus err = noErr;
+ ItemCount nitems;
+ int i;
+ char *s;
+
+ if(appleclip == NULL)
+ return nil;
+ // Wow. This is ridiculously complicated.
+ PasteboardSynchronize(appleclip);
+ if((err = PasteboardGetItemCount(appleclip, &nitems)) != noErr) {
+ fprint(2, "apple pasteboard GetItemCount failed - Error %d\n", err);
+ return 0;
+ }
+
+ // Yes, based at 1. Silly API.
+ for(i = 1; i <= nitems; i++) {
+ PasteboardItemID itemID;
+ CFArrayRef flavorTypeArray;
+ CFIndex flavorCount;
+
+ if((err = PasteboardGetItemIdentifier(appleclip, i, &itemID)) != noErr){
+ fprint(2, "Can't get pasteboard item identifier: %d\n", err);
+ return 0;
+ }
+
+ if((err = PasteboardCopyItemFlavors(appleclip, itemID, &flavorTypeArray))!=noErr){
+ fprint(2, "Can't copy pasteboard item flavors: %d\n", err);
+ return 0;
+ }
+
+ flavorCount = CFArrayGetCount(flavorTypeArray);
+ CFIndex flavorIndex;
+ for(flavorIndex = 0; flavorIndex < flavorCount; ++flavorIndex){
+ CFStringRef flavorType;
+ flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
+ if (UTTypeConformsTo(flavorType, CFSTR("public.utf16-plain-text"))){
+ if((err = PasteboardCopyItemFlavorData(appleclip, itemID,
+ CFSTR("public.utf16-plain-text"), &cfdata)) != noErr){
+ fprint(2, "apple pasteboard CopyItem failed - Error %d\n", err);
+ return 0;
+ }
+ CFIndex length = CFDataGetLength(cfdata);
+ if (length > sizeof rsnarf) length = sizeof rsnarf;
+ CFDataGetBytes(cfdata, CFRangeMake(0, length), (uint8_t *)rsnarf);
+ snprint(snarf, sizeof snarf, "%.*S", length/sizeof(Rune), rsnarf);
+ for(s = snarf; *s; s++)
+ if(*s == '\r')
+ *s = '\n';
+ CFRelease(cfdata);
+ return strdup(snarf);
+ }
+ }
+ }
+ return 0;
+}
+
+int
+clipwrite(char *snarf)
+{
+ CFDataRef cfdata;
+ PasteboardSyncFlags flags;
+
+ if(appleclip == NULL)
+ return 0;
+ runeseprint(rsnarf, rsnarf+nelem(rsnarf), "%s", snarf);
+ if(PasteboardClear(appleclip) != noErr){
+ fprint(2, "apple pasteboard clear failed\n");
+ return 0;
+ }
+ flags = PasteboardSynchronize(appleclip);
+ if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
+ fprint(2, "apple pasteboard cannot assert ownership\n");
+ return 0;
+ }
+ cfdata = CFDataCreate(kCFAllocatorDefault, (uchar*)rsnarf, runestrlen(rsnarf)*2);
+ if(cfdata == nil){
+ fprint(2, "apple pasteboard cfdatacreate failed\n");
+ return 0;
+ }
+ if(PasteboardPutItemFlavor(appleclip, (PasteboardItemID)1,
+ CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
+ fprint(2, "apple pasteboard putitem failed\n");
+ CFRelease(cfdata);
+ return 0;
+ }
+ CFRelease(cfdata);
+ return 1;
+}
+
+void
+setpointer(int x, int y)
+{
+ CGPoint pnt;
+
+ pnt.x = x + winRect.left;
+ pnt.y = y + winRect.top;
+ CGWarpMouseCursorPosition(pnt);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+ USED(c);
+ /* removed, pending extensive change for newer MacOS X */
+}
--- /dev/null
+++ b/emu/NOTICE
@@ -1,0 +1,32 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory. If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below. It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+ Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+ Portions Copyright © 1997-1999 Vita Nuova Limited
+ Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+ Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+ Portions Copyright © 2005 Russ Cox, MIT
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
--- /dev/null
+++ b/emu/NetBSD/asm-386.S
@@ -1,0 +1,101 @@
+ .file "asm-NetBSD-386.S"
+#include <sys/syscall.h>
+
+/*
+ * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg)
+ */
+
+ .type executeonnewstack,@function
+ .global executeonnewstack
+executeonnewstack:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+
+ movl 8(%ebp), %esi /* get tos */
+ subl $4, %esi
+ movl 16(%ebp), %eax
+ movl %eax, (%esi) /* stash arg on new stack */
+ subl $4, %esi
+ movl 12(%ebp), %eax
+ movl %eax, (%esi) /* stash tramp on new stack */
+ mov %esi, %esp /* swap stacks pronto */
+ popl %eax /* recover the tramp address */
+ call *%eax /* and jump to it (ho ho) */
+
+ /* if we return here, tramp didn't do its job */
+
+ addl $8, %esp /* clean up for pose value */
+
+ leal SYS_exit, %eax
+ int $0x80
+
+/*
+ * unlockandexit(int *key)
+ *
+ * NB: the return status may be rubbish if the stack is reused
+ * between the unlock and the system call, but this should
+ * not matter since no task is waiting for the result
+ */
+
+ .type unlockandexit,@function
+ .global unlockandexit
+unlockandexit:
+ pushl %ebp
+ movl %esp, %ebp
+
+ movl 8(%ebp), %esi /* get the key address */
+ pushl $0 /* exit status 0 */
+ movl $0, %eax /* unlock the stack allocator */
+ movl %eax, (%esi)
+ leal SYS_exit, %eax /* call exit */
+ int $0x80
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .type umult,@function
+ .global umult
+umult:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+
+ movl 8(%ebp), %eax
+ movl 12(%ebp), %ebx
+ mull %ebx
+ movl 16(%ebp), %ebx
+ movl %edx, (%ebx)
+
+ popl %ebx
+ popl %ebp
+ ret
+
+ .type FPsave,@function
+ .global FPsave
+FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .type FPrestore,@function
+ .global FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .type _tas,@function
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/NetBSD/audio.c
@@ -1,0 +1,547 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/soundcard.h>
+
+#define Audio_Mic_Val SOUND_MIXER_MIC
+#define Audio_Linein_Val SOUND_MIXER_LINE
+
+#define Audio_Speaker_Val SOUND_MIXER_SPEAKER
+#define Audio_Headphone_Val SOUND_MIXER_PHONEOUT
+#define Audio_Lineout_Val SOUND_MIXER_VOLUME
+
+#define Audio_Pcm_Val AFMT_S16_LE
+#define Audio_Ulaw_Val AFMT_MU_LAW
+#define Audio_Alaw_Val AFMT_A_LAW
+
+#include "audio-tbls.c"
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+static int debug;
+
+#define AUDIO_FILE_STRING "/dev/dsp"
+
+enum {
+ A_Pause,
+ A_UnPause
+};
+
+enum {
+ A_In,
+ A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static int audio_file = -1; /* file in/out */
+static int audio_file_in = -1; /* copy of above when opened O_READ/O_RDWR */
+static int audio_file_out = -1; /* copy of above when opened O_WRITE/O_RDWR */
+
+static int audio_swap_flag = 0; /* endian swap */
+
+static int audio_in_pause = A_UnPause;
+
+static Audio_t av;
+static int mixerleftvol[32];
+static int mixerrightvol[32];
+
+static int audio_enforce(Audio_t*);
+static int audio_open(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+ int i;
+ static ushort flag = 1;
+
+ audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */
+ audio_info_init(&av);
+ for (i = 0; i < 32; i++)
+ mixerleftvol[i] = mixerrightvol[i] = 100;
+}
+
+void
+audio_ctl_init(void)
+{
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ char ebuf[ERRMAX];
+
+ if (debug)
+ print("audio_file_open(0x%.8lux, %d)\n", c, omode);
+ switch(omode){
+ case OREAD:
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file;
+ poperror();
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ qunlock(&inlock);
+ nexterror();
+ }
+ if(audio_file_in >= 0 || audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+ if (debug)
+ print("audio_file_open: success\nin %d out %d both %d\n",
+ audio_file_out, audio_file_in, audio_file);
+}
+
+void
+audio_ctl_open(Chan *c, int omode)
+{
+ USED(c);
+ USED(omode);
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_out < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ qunlock(&outlock);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_in < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ audio_file_out = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ close(audio_file);
+ audio_file_in = audio_file_out = audio_file = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+}
+
+void
+audio_ctl_close(Chan *c)
+{
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long ba, status, chunk, total;
+ char *pva = (char *) va;
+
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(! audio_pause_in(audio_file_in, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count) {
+ chunk = count - total;
+ osenter();
+ status = read(audio_file_in, pva + total, chunk);
+ osleave();
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(pva, count);
+
+ poperror();
+ qunlock(&inlock);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ if (debug > 1)
+ print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
+ c, va, count, offset);
+
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(audio_file_out < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(va, count);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count) {
+ chunk = min(bufsz, count - total);
+ osenter();
+ status = write(audio_file_out, va, chunk);
+ osleave();
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ return count;
+}
+
+static int
+audio_open(void)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ if (debug)
+ print("audio_open: failed to set blocking\n");
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ if (debug)
+ print("audio_open: blocking set\n");
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = 0;
+
+ if(! audio_set_info(fd, &av.in, A_In)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ int fd;
+ int ff;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if (!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if (!audio_enforce(&tmpav))
+ error(Ebadarg);
+
+ qlock(&inlock);
+ if (waserror()) {
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+ if (!audio_pause_in(audio_file_in, A_Pause))
+ error(Ebadarg);
+ if (!audio_flush(audio_file_in, A_In))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&inlock);
+
+ qlock(&outlock);
+ if (waserror()) {
+ qunlock(&outlock);
+ nexterror();
+ }
+ if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+ if (!audio_pause_out(audio_file_out))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&outlock);
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+doioctl(int fd, int ctl, int *info)
+{
+ int status;
+ osenter();
+ status = ioctl(fd, ctl, info); /* qlock and load general stuff */
+ osleave();
+ if (status < 0)
+ print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
+ return status;
+}
+
+static int
+choosefmt(Audio_d *i)
+{
+ int newbits, newenc;
+
+ newbits = i->bits;
+ newenc = i->enc;
+ switch (newenc) {
+ case Audio_Alaw_Val:
+ if (newbits == 8)
+ return AFMT_A_LAW;
+ break;
+ case Audio_Ulaw_Val:
+ if (newbits == 8)
+ return AFMT_MU_LAW;
+ break;
+ case Audio_Pcm_Val:
+ if (newbits == 8)
+ return AFMT_U8;
+ else if (newbits == 16)
+ return AFMT_S16_LE;
+ break;
+ }
+ return -1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status;
+ int unequal_stereo = 0;
+
+ if(fd < 0)
+ return 0;
+
+ /* fmt */
+ if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
+ int oldfmt, newfmt;
+ oldfmt = AFMT_QUERY;
+ if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
+ return 0;
+ if (debug)
+ print("audio_set_info: current format 0x%.8lux\n", oldfmt);
+ newfmt = choosefmt(i);
+ if (debug)
+ print("audio_set_info: new format 0x%.8lux\n", newfmt);
+ if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
+ return 0;
+ }
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG) {
+ int channels = i->chan;
+ if (debug)
+ print("audio_set_info: new channels %d\n", channels);
+ if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
+ || channels != i->chan)
+ return 0;
+ }
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG) {
+ int speed = i->rate;
+ if (debug)
+ print("audio_set_info: new speed %d\n", speed);
+ if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
+ return 0;
+ }
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
+ int val;
+ if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
+ mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
+ if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
+ mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
+ val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
+ doioctl(fd, MIXER_WRITE(i->dev), &val);
+ }
+
+ if (i->flags & AUDIO_DEV_FLAG) {
+ }
+
+ return 1;
+}
+
+void
+audio_swap_endian(char *p, int n)
+{
+ int b;
+
+ while (n > 1) {
+ b = p[0];
+ p[0] = p[1];
+ p[1] = b;
+ p += 2;
+ n -= 2;
+ }
+}
+
+static int
+audio_pause_out(int fd)
+{
+ USED(fd);
+ return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+ USED(fd);
+ USED(f);
+ return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+ int x;
+ return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+ if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) &&
+ (t->in.rate != 8000 || t->in.chan != 1))
+ return 0;
+ if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) &&
+ (t->out.rate != 8000 || t->out.chan != 1))
+ return 0;
+ return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
--- /dev/null
+++ b/emu/NetBSD/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgid(0, getpid());
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/NetBSD/deveia.c
@@ -1,0 +1,39 @@
+/*
+ * NetBSD serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/dty00",
+ "/dev/dty01",
+ "/dev/dty02",
+ "/dev/dty03",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {115200, B115200},
+ {230400, B230400},
+ {-1, -1}
+};
+
--- /dev/null
+++ b/emu/NetBSD/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/NetBSD/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/NetBSD/emu-g
@@ -1,0 +1,102 @@
+env
+ X11LIBS=
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ math
+
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ void setpointer(int x, int y){USED(x); USED(y);}
+ ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/NetBSD/emu.c
@@ -1,0 +1,88 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+
+
+#include "emu.root.h"
+
+ulong ndevs = 29;
+
+extern Dev rootdevtab;
+extern Dev consdevtab;
+extern Dev envdevtab;
+extern Dev mntdevtab;
+extern Dev pipedevtab;
+extern Dev progdevtab;
+extern Dev profdevtab;
+extern Dev srvdevtab;
+extern Dev dupdevtab;
+extern Dev ssldevtab;
+extern Dev capdevtab;
+extern Dev fsdevtab;
+extern Dev cmddevtab;
+extern Dev indirdevtab;
+extern Dev drawdevtab;
+extern Dev pointerdevtab;
+extern Dev snarfdevtab;
+extern Dev ipdevtab;
+extern Dev eiadevtab;
+extern Dev audiodevtab;
+extern Dev memdevtab;
+Dev* devtab[]={
+ &rootdevtab,
+ &consdevtab,
+ &envdevtab,
+ &mntdevtab,
+ &pipedevtab,
+ &progdevtab,
+ &profdevtab,
+ &srvdevtab,
+ &dupdevtab,
+ &ssldevtab,
+ &capdevtab,
+ &fsdevtab,
+ &cmddevtab,
+ &indirdevtab,
+ &drawdevtab,
+ &pointerdevtab,
+ &snarfdevtab,
+ &ipdevtab,
+ &eiadevtab,
+ &audiodevtab,
+ &memdevtab,
+ nil,
+ nil,
+ nil,
+ nil,
+ nil,
+ nil,
+ nil,
+ nil,
+ nil,
+};
+
+void links(void){
+}
+
+extern void sysmodinit(void);
+extern void drawmodinit(void);
+extern void tkmodinit(void);
+extern void mathmodinit(void);
+extern void srvmodinit(void);
+extern void keyringmodinit(void);
+extern void loadermodinit(void);
+extern void freetypemodinit(void);
+void modinit(void){
+ sysmodinit();
+ drawmodinit();
+ tkmodinit();
+ mathmodinit();
+ srvmodinit();
+ keyringmodinit();
+ loadermodinit();
+ freetypemodinit();
+}
+
+char* conffile = "emu";
+ulong kerndate = KERNDATE;
--- /dev/null
+++ b/emu/NetBSD/mkfile
@@ -1,0 +1,49 @@
+SYSTARG=NetBSD
+OBJTYPE=386
+<../../mkconfig
+SYSTARG=NetBSD
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+# can remove or override X11LIBS using env section in config files
+X11LIBS= -L/usr/X11R7/lib -R/usr/X11R7/lib -lXext -lX11
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= ${X11LIBS} -lossaudio -lm
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/NetBSD/os.c
@@ -1,0 +1,537 @@
+#include <sys/types.h>
+#include <time.h>
+#include <termios.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+/* For dynamic linking init/fini code that needs malloc */
+void (*coherence)(void) = nofence;
+
+
+enum
+{
+ DELETE = 0x7f,
+ CTRLC = 'C'-'@',
+ NSTACKSPERALLOC = 16,
+ X11STACK= 256*1024
+};
+char *hosttype = "NetBSD";
+
+static void *stackalloc(Proc *p, void **tos);
+static void stackfreeandexit(void *stack);
+
+void executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg);
+void unlockandexit(ulong *key);
+
+extern int dflag;
+
+int gidnobody = -1;
+int uidnobody = -1;
+static struct termios tinit;
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+ void *kstack;
+
+ lock(&procs.l);
+ if(up->prev)
+ up->prev->next = up->next;
+ else
+ procs.head = up->next;
+
+ if(up->next)
+ up->next->prev = up->prev;
+ else
+ procs.tail = up->prev;
+ unlock(&procs.l);
+
+ if(0)
+ print("pexit: %s: %s\n", up->text, msg);
+
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ kstack = up->kstack;
+ free(up->prog);
+ free(up);
+ if(kstack != nil)
+ stackfreeandexit(kstack);
+}
+
+int
+tramp(void *arg)
+{
+ Proc *p;
+ p = arg;
+ p->pid = p->sigid = getpid();
+ (*p->func)(p->arg);
+ pexit("{Tramp}", 0);
+ return 0;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ int pid;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ void *tos;
+
+ p = newproc();
+ if(0)
+ print("start %s:%.8lx\n", name, p);
+ if(p == nil) {
+ print("kproc(%s): no memory", name);
+ panic("kproc: no memory");
+ }
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ if(flags & KPX11){
+ p->kstack = nil; /* never freed; also up not defined */
+ tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*);
+ }else
+ p->kstack = stackalloc(p, &tos);
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if (__clone(tramp, tos, /*CLONE_PTRACE|*/CLONE_VM|CLONE_FS|CLONE_FILES|SIGCHLD, p) <= 0) {
+ fprint(2, "emu: clone failed: %s\n", strerror(errno));
+ panic("kproc: clone failed");
+ }
+}
+
+/*
+ * TO DO:
+ * To get pc on trap, use sigaction instead of signal and
+ * examine its siginfo structure
+ */
+
+/*
+static void
+diserr(char *s, int pc)
+{
+ char buf[ERRMAX];
+
+ snprint(buf, sizeof(buf), "%s: pc=0x%lux", s, pc);
+ disfault(nil, buf);
+}
+*/
+
+static void
+trapILL(int signo)
+{
+ USED(signo);
+ disfault(nil, "Illegal instruction");
+}
+
+static void
+trapBUS(int signo)
+{
+ USED(signo);
+ disfault(nil, "Bus error");
+}
+
+static void
+trapSEGV(int signo)
+{
+ USED(signo);
+ disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(int signo)
+{
+ char buf[64];
+ USED(signo);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
+ disfault(nil, buf);
+}
+
+static void
+trapUSR1(int signo)
+{
+ int intwait;
+
+ USED(signo);
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+
+ if(intwait == 0) /* Not posted so it's a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+/* called to wake up kproc blocked on a syscall */
+void
+oshostintr(Proc *p)
+{
+ kill(p->sigid, SIGUSR1);
+}
+
+static void
+trapUSR2(int signo)
+{
+ USED(signo);
+ /* we've done our work of interrupting sigsuspend */
+}
+
+void
+osblock(void)
+{
+ sigset_t mask;
+
+ sigprocmask(SIG_SETMASK, NULL, &mask);
+ sigdelset(&mask, SIGUSR2);
+ sigsuspend(&mask);
+}
+
+void
+osready(Proc *p)
+{
+ if(kill(p->sigid, SIGUSR2) < 0)
+ fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ error("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+ struct termios t;
+ struct sigaction act;
+ sigset_t mask;
+ struct passwd *pw;
+ Proc *p;
+ void *tos;
+ char sys[64];
+
+ setsid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+
+ pw = getpwnam("nobody");
+ if(pw != nil) {
+ uidnobody = pw->pw_uid;
+ gidnobody = pw->pw_gid;
+ }
+
+ if(dflag == 0)
+ termset();
+
+ memset(&act, 0 , sizeof(act));
+ act.sa_handler = trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR2);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ memset(&act, 0 , sizeof(act));
+ act.sa_handler = trapUSR2;
+ sigaction(SIGUSR2, &act, nil);
+
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &act, nil);
+
+ /*
+ * For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGPIPE, SIG_IGN);
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+
+ if(sflag == 0) {
+ act.sa_handler = trapBUS;
+ sigaction(SIGBUS, &act, nil);
+ act.sa_handler = trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_handler = trapSEGV;
+ sigaction(SIGSEGV, &act, nil);
+ act.sa_handler = trapFPE;
+ sigaction(SIGFPE, &act, nil);
+ }
+
+ p = newproc();
+ p->kstack = stackalloc(p, &tos);
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ p->env->uid = getuid();
+ p->env->gid = getgid();
+
+ executeonnewstack(tos, emuinit, imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ buf[0] = 'H' - '@';
+ break;
+ case CTRLC:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return 0;
+
+ if(sec0 == 0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec/1000;
+ time.tv_nsec= (milsec%1000)*1000000;
+ nanosleep(&time, NULL);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ sched_yield();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+static struct {
+ Lock l;
+ void *free;
+} stacklist;
+
+static void
+_stackfree(void *stack)
+{
+ *((void **)stack) = stacklist.free;
+ stacklist.free = stack;
+}
+
+static void
+stackfreeandexit(void *stack)
+{
+ lock(&stacklist.l);
+ _stackfree(stack);
+ unlockandexit(&stacklist.l.val);
+}
+
+static void *
+stackalloc(Proc *p, void **tos)
+{
+ void *rv;
+ lock(&stacklist.l);
+ if (stacklist.free == 0) {
+ int x;
+ /*
+ * obtain some more by using sbrk()
+ */
+ void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
+ if (more == 0)
+ panic("stackalloc: no more stacks");
+ /*
+ * align to KSTACK
+ */
+ more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
+ /*
+ * free all the new stacks onto the freelist
+ */
+ for (x = 0; x < NSTACKSPERALLOC; x++)
+ _stackfree((char *)more + KSTACK * x);
+ }
+ rv = stacklist.free;
+ stacklist.free = *(void **)rv;
+ unlock(&stacklist.l);
+ *tos = rv + KSTACK - sizeof(void *);
+ *(Proc **)rv = p;
+ return rv;
+}
+
+int
+segflush(void *a, ulong n)
+{
+ USED(a);
+ USED(n);
+ return 0;
+}
--- /dev/null
+++ b/emu/Nt/audio.c
@@ -1,0 +1,810 @@
+#define Unknown win_Unknown
+#include <windows.h>
+#include <mmsystem.h>
+#undef Unknown
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#define Audio_Mic_Val 0
+#define Audio_Linein_Val -1
+
+#define Audio_Speaker_Val 0
+#define Audio_Headphone_Val -1
+#define Audio_Lineout_Val -1
+
+#define Audio_Pcm_Val WAVE_FORMAT_PCM
+#define Audio_Ulaw_Val (WAVE_FORMAT_PCM+1)
+#define Audio_Alaw_Val (WAVE_FORMAT_PCM+2)
+
+#define Audio_Max_Queue 8
+
+#define BUFLEN 1000
+
+#define INISOPEN 0x00000002 // the microphone is open
+#define OUTISOPEN 0x00000004 // the speaker is open
+#define INPUTISGOING 0x00000020 // microphone is being recorded/read
+
+#include "audio.h"
+#include "audio-tbls.c"
+
+static int debug = 0;
+
+/* TO DO: sensible expression of double-buffering */
+#define Ping 0
+#define Pong 1
+
+static HWAVEIN audio_file_in;
+static HWAVEOUT audio_file_out;
+
+static long out_buf_count;
+
+typedef struct _awin {
+ WAVEHDR hdr;
+ long sz;
+ char* ptr;
+ char data[Audio_Max_Buf];
+} AWin;
+
+static AWin audio_ping;
+static AWin audio_pong;
+
+static long paddle = Ping;
+static int ping_is_filling;
+static int pong_is_filling;
+
+static long audio_flags = 0;
+static int audio_init = 0;
+
+static QLock flag_lock;
+
+static Audio_t av;
+
+static HANDLE outlock;
+static HANDLE inlock;
+
+static int audio_open_in(HWAVEIN*, Audio_d*);
+static int audio_open_out(HWAVEOUT*, Audio_d*);
+static void audio_close_in(void);
+static void audio_close_out(void);
+static void CALLBACK waveInProc(HWAVEIN, UINT, DWORD, DWORD, DWORD);
+static void CALLBACK waveOutProc(HWAVEOUT, UINT, DWORD, DWORD, DWORD);
+
+#define AUDIOIN 0
+#define AUDIOOUT 1
+
+/*
+* Error routines
+*/
+static int
+audioerror(unsigned int code, int in_out, char *msg)
+{
+ char errorText[MAXERRORLENGTH];
+
+ if (code != MMSYSERR_NOERROR) {
+ switch(in_out) {
+ case AUDIOIN:
+ waveInGetErrorText(code, errorText, sizeof(errorText));
+ //print("ERROR -- %s: %s\n", msg, errorText);
+ return(-1);
+ case AUDIOOUT:
+ waveOutGetErrorText(code, errorText, sizeof(errorText));
+ //print("ERROR -- %s: %s\n", msg, errorText);
+ return(-1);
+ default:
+ print("%s: Unknown device\n", msg);
+ }
+ }
+ //print("TRACE %s\n", msg);
+ return 0;
+}
+
+void
+audio_file_init(void)
+{
+ audio_info_init(&av);
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ int in_is_open = 0;
+
+ switch(omode){
+ case OREAD:
+ qlock(&flag_lock);
+
+ if(waserror()) {
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ if(audio_flags & INISOPEN)
+ error(Einuse);
+
+ inlock = CreateMutex(NULL, FALSE, NULL);
+ if(inlock == NULL)
+ error(Einuse);
+
+ if(!audio_open_in(&audio_file_in, &av.in) ) {
+ CloseHandle(inlock);
+ error(Ebadarg);
+ }
+
+ ping_is_filling = 0;
+ pong_is_filling = 0;
+ paddle = Ping;
+ audio_flags |= INISOPEN;
+
+ poperror();
+ qunlock(&flag_lock);
+ break;
+ case OWRITE:
+ qlock(&flag_lock);
+ if(waserror()){
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ if(audio_flags & OUTISOPEN)
+ error(Einuse);
+
+ outlock = CreateMutex(NULL, FALSE, NULL);
+ if(outlock == NULL)
+ error(Einuse);
+
+ if(!audio_open_out(&audio_file_out, &av.out) ) {
+ CloseHandle(outlock);
+ error(Ebadarg);
+ }
+
+ out_buf_count = 0;
+ audio_flags |= OUTISOPEN;
+
+ poperror();
+ qunlock(&flag_lock);
+ break;
+ case ORDWR:
+ qlock(&flag_lock);
+ if(waserror()){
+ qunlock(&flag_lock);
+ if(in_is_open)
+ audio_close_in();
+ nexterror();
+ }
+
+ if((audio_flags & INISOPEN) || (audio_flags & OUTISOPEN))
+ error(Einuse);
+
+ if(!audio_open_in(&audio_file_in, &av.in) )
+ error(Ebadarg);
+
+ in_is_open = 1;
+
+ if(!audio_open_out(&audio_file_out, &av.out)) {
+ CloseHandle(outlock);
+ error(Ebadarg);
+ }
+
+ inlock = CreateMutex(NULL, FALSE, NULL);
+ if(inlock == NULL)
+ error(Einuse);
+
+ outlock = CreateMutex(NULL, FALSE, NULL);
+ if(outlock == NULL) {
+ CloseHandle(inlock);
+ error(Einuse);
+ }
+
+ audio_flags |= INISOPEN;
+ audio_flags |= OUTISOPEN;
+ ping_is_filling = 0;
+ pong_is_filling = 0;
+ paddle = Ping;
+ out_buf_count = 0;
+
+ poperror();
+ qunlock(&flag_lock);
+ break;
+ default:
+ error(Egreg);
+ }
+}
+
+static int
+audio_open_in(HWAVEIN* h, Audio_d* d)
+{
+ HWAVEIN th;
+ WAVEFORMATEX format;
+
+ format.wFormatTag = d->enc;
+ format.nChannels = d->chan;
+ format.nSamplesPerSec = d->rate;
+ format.wBitsPerSample = d->bits;
+ format.nBlockAlign = (d->chan * d->bits) / Bits_Per_Byte;
+ format.nAvgBytesPerSec =
+ format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ if (audioerror(
+ waveInOpen(&th, WAVE_MAPPER, &format, (DWORD)waveInProc, 0, CALLBACK_FUNCTION),
+ AUDIOIN,
+ "cannot open microphone/line-in") == 0) {
+ *h = th;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+audio_open_out(HWAVEOUT* h, Audio_d* d)
+{
+ unsigned int code;
+ HWAVEOUT th;
+ WAVEFORMATEX format;
+
+ format.wFormatTag = d->enc;
+ format.nChannels = d->chan;
+ format.nSamplesPerSec = d->rate;
+ format.wBitsPerSample = d->bits;
+ format.nBlockAlign = (d->chan * d->bits) / Bits_Per_Byte;
+ format.nAvgBytesPerSec =
+ format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ code = waveOutOpen(&th, WAVE_MAPPER, &format, (DWORD)waveOutProc, 0, CALLBACK_FUNCTION);
+
+ if (audioerror(code, AUDIOOUT, "cannot open speaker/line-out") == 0) {
+ out_buf_count = 0;
+ *h = th;
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&flag_lock);
+ audio_close_in();
+ audio_flags &= ~(INISOPEN|INPUTISGOING);
+ CloseHandle(inlock);
+ qunlock(&flag_lock);
+ break;
+ case OWRITE:
+ qlock(&flag_lock);
+ audio_close_out();
+ audio_flags &= ~OUTISOPEN;
+ CloseHandle(outlock);
+ qunlock(&flag_lock);
+ break;
+ case ORDWR:
+ qlock(&flag_lock);
+ audio_close_in();
+ audio_close_out();
+
+ audio_flags &= ~(INISOPEN|INPUTISGOING|OUTISOPEN);
+
+ CloseHandle(outlock);
+ CloseHandle(inlock);
+ qunlock(&flag_lock);
+ break;
+ }
+}
+
+static void
+audio_close_in()
+{
+ audioerror(waveInStop(audio_file_in), AUDIOIN, "audio_close_in Stop");
+ audioerror(waveInReset(audio_file_in), AUDIOIN, "audio_close_in Reset");
+
+ audioerror(waveInUnprepareHeader(audio_file_in, &audio_ping.hdr,
+ sizeof(WAVEHDR)), AUDIOIN, "in un prepare ping header");
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ audioerror(waveInUnprepareHeader(audio_file_in, &audio_pong.hdr,
+ sizeof(WAVEHDR)), AUDIOIN, "in un prepare pong header");
+ audio_pong.sz = 0;
+ audio_pong.ptr = &audio_pong.data[0];
+
+ audioerror(waveInClose(audio_file_in), AUDIOIN, "in close");
+
+}
+
+static void
+audio_close_out()
+{
+Again:
+ WaitForSingleObject(outlock, INFINITE);
+ while(out_buf_count > 0) {
+ ReleaseMutex(outlock);
+ sleep(0);
+ goto Again;
+ }
+ ReleaseMutex(outlock);
+
+ audioerror(waveOutReset(audio_file_out), AUDIOOUT, "close wave out reset");
+ audioerror(waveOutClose(audio_file_out), AUDIOOUT, "closing out device");
+}
+
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ MMRESULT status;
+ long len = av.in.buf * Audio_Max_Buf / Audio_Max_Val;
+ char *v = (char *) va;
+ char *p;
+ long ba, n, chunk, total;
+
+
+ qlock(&flag_lock);
+ WaitForSingleObject(inlock, INFINITE);
+
+ if(waserror()) {
+ audioerror(waveInStop(audio_file_in), AUDIOIN,
+ "audio_file_read Stop 1");
+ audioerror(waveInReset(audio_file_in), AUDIOIN,
+ "audio_file_read Reset 1");
+ audioerror(waveInUnprepareHeader(audio_file_in,
+ &audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "in unprepare ping");
+ audioerror(waveInUnprepareHeader(audio_file_in,
+ &audio_pong.hdr, sizeof(WAVEHDR)),
+ AUDIOIN, "in unprepare pong");
+
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ audio_pong.sz = 0;
+ audio_pong.ptr = &audio_pong.data[0];
+
+ ping_is_filling = pong_is_filling = 0;
+ paddle = Ping;
+
+ qunlock(&flag_lock);
+ ReleaseMutex(inlock);
+
+ nexterror();
+ }
+
+ if(!(audio_flags & INISOPEN))
+ error(Eperm);
+
+ /* check for block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(len < 1 || count % ba)
+ error(Ebadarg);
+
+ if(!(audio_flags & INPUTISGOING)) {
+ if(audioerror(waveInStart(audio_file_in), AUDIOIN,
+ "in start") == -1)
+ error(Eio);
+
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ audio_ping.hdr.lpData = audio_ping.ptr;
+ audio_ping.hdr.dwBufferLength = len;
+ audio_ping.hdr.dwUser = Ping;
+ audio_ping.hdr.dwFlags = 0;
+
+ status = waveInPrepareHeader(audio_file_in, &audio_ping.hdr,
+ sizeof(WAVEHDR));
+
+ if (audioerror(status, AUDIOIN, "in prepare header") == -1)
+ error(Eio);
+
+ audio_pong.sz = 0;
+ audio_pong.ptr = &audio_pong.data[0];
+ audio_pong.hdr.lpData = audio_pong.ptr;
+ audio_pong.hdr.dwBufferLength = len;
+ audio_pong.hdr.dwUser = Pong;
+ audio_pong.hdr.dwFlags = 0;
+
+ status = waveInPrepareHeader(audio_file_in, &audio_pong.hdr,
+ sizeof(WAVEHDR));
+
+ if (audioerror(status, AUDIOIN, "in prepare header") == -1)
+ error(Eio);
+
+ status = waveInAddBuffer(audio_file_in, &audio_ping.hdr,
+ sizeof(WAVEHDR));
+ if (audioerror(status, AUDIOIN, "file_read Add Buffer")== -1){
+ waveInUnprepareHeader(audio_file_in, &audio_ping.hdr,
+ sizeof(WAVEHDR));
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ error(Eio);
+ }
+
+ ping_is_filling = 1;
+ pong_is_filling = 0;
+ paddle = Ping;
+ audio_flags |= INPUTISGOING;
+ }
+ poperror();
+ ReleaseMutex(inlock);
+
+ total = 0;
+
+Draining:
+
+ WaitForSingleObject(inlock, INFINITE);
+ if(waserror()) {
+ audioerror(waveInStop(audio_file_in), AUDIOIN,
+ "audio_file_read Stop 2");
+ audioerror(waveInReset(audio_file_in), AUDIOIN,
+ "audio_file_read Reset 2");
+ audioerror(waveInUnprepareHeader(audio_file_in,
+ &audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "in unprepare ping");
+ audioerror(waveInUnprepareHeader(audio_file_in,
+ &audio_pong.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "in unprepare pong");
+
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ audio_pong.sz = 0;
+ audio_pong.ptr = &audio_pong.data[0];
+
+ audio_flags &= ~INPUTISGOING;
+
+ ReleaseMutex(inlock);
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ while((total < count) && ((audio_ping.sz > 0) || (audio_pong.sz > 0))) {
+ n = paddle == Ping ? audio_ping.sz : audio_pong.sz;
+ p = paddle == Ping ? audio_ping.ptr : audio_pong.ptr;
+
+ chunk = min(n, count - total);
+
+ memmove(v+total, p , chunk);
+
+ total += chunk;
+
+ if(paddle == Ping) {
+ if(!pong_is_filling) {
+
+ if(audioerror(waveInAddBuffer(audio_file_in,
+ &audio_pong.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "draining ping calling add buffer pong") == -1)
+ error(Eio);
+
+ pong_is_filling = 1;
+ }
+
+ audio_ping.sz -= chunk;
+ if(audio_ping.sz > 0) {
+ audio_ping.ptr += chunk;
+ } else {
+ audio_ping.ptr = &audio_ping.data[0];
+ ping_is_filling = 0;
+ paddle = Pong;
+ }
+ } else {
+ if(!ping_is_filling) {
+
+ if(audioerror(waveInAddBuffer(audio_file_in,
+ &audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "draining pong calling add buffer ping") == -1)
+ error(Eio);
+
+ ping_is_filling = 1;
+ }
+
+ audio_pong.sz -= chunk;
+ if(audio_pong.sz > 0) {
+ audio_pong.ptr += chunk;
+ } else {
+ audio_pong.ptr = &audio_pong.data[0];
+ pong_is_filling = 0;
+ paddle = Ping;
+ }
+ }
+ }
+
+ poperror();
+
+ ReleaseMutex(inlock);
+
+ if(total == count) {
+ qunlock(&flag_lock);
+ return count;
+ }
+
+Filling:
+ WaitForSingleObject(inlock, INFINITE);
+ while((audio_ping.sz < 1) && (audio_pong.sz < 1)) {
+ ReleaseMutex(inlock);
+ sleep(0);
+ goto Filling;
+ }
+ ReleaseMutex(inlock);
+
+ goto Draining;
+}
+
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ MMRESULT status;
+ WAVEHDR *hHdr = (WAVEHDR *) NULL;
+ char *hData = NULL;
+ char *p = (char *) va;
+ long ba;
+ long bufsz;
+ long chunk;
+ long total;
+
+ qlock(&flag_lock);
+ if(waserror()){
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ if(!(audio_flags & OUTISOPEN))
+ error(Eperm);
+
+ /* check for block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz < 1)
+ error(Ebadarg);
+
+ total = 0;
+
+ while(total < count) {
+
+Again:
+ chunk = min(bufsz, count - total);
+
+Drain:
+ WaitForSingleObject(outlock, INFINITE);
+ while(out_buf_count > bufsz) {
+ ReleaseMutex(outlock);
+ sleep(0);
+ goto Drain;
+ }
+
+ if(out_buf_count == 0)
+ audioerror(waveOutReset(audio_file_out), AUDIOOUT, "wave out reset");
+ ReleaseMutex(outlock);
+
+ /*
+ * allocate and lock the memory for the wave header
+ * and data blocks
+ */
+ hHdr = (WAVEHDR *) malloc(sizeof(WAVEHDR));
+ if (!hHdr)
+ error(Enomem);
+
+ hData = malloc(chunk);
+ if (!hData) {
+ free(hHdr);
+ error(Enomem);
+ }
+
+ /*
+ * initialize the wave header struct
+ */
+
+ /*
+ * copy user data into write Q
+ */
+ memmove(hData, p+total, chunk);
+
+ hHdr->lpData = hData;
+ hHdr->dwBufferLength = chunk;
+ hHdr->dwBytesRecorded = 0;
+ hHdr->dwUser = chunk;
+ hHdr->dwFlags = 0;
+ hHdr->dwLoops = 0;
+ hHdr->lpNext = 0;
+ hHdr->reserved = 0;
+
+ status = waveOutPrepareHeader(audio_file_out, hHdr, sizeof(WAVEHDR));
+
+ if (audioerror(status, AUDIOOUT, "out prepare header") == -1) {
+ free(hHdr);
+ free(hData);
+ error(Eio);
+ }
+
+ status =
+ waveOutWrite(audio_file_out, hHdr, sizeof(WAVEHDR));
+
+ if (audioerror(status, AUDIOOUT, "out write data") == -1) {
+ waveOutUnprepareHeader(audio_file_out, hHdr, sizeof(WAVEHDR));
+ free(hHdr);
+ free(hData);
+ error(Eio);
+ }
+
+ WaitForSingleObject(outlock, INFINITE);
+ out_buf_count += chunk;
+ ReleaseMutex(outlock);
+
+ total += chunk;
+
+ }
+
+ poperror();
+ qunlock(&flag_lock);
+ osmillisleep(1); /* hack to get around thread scheduler */
+
+ return count;
+}
+
+void CALLBACK
+waveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
+{
+ LPWAVEHDR hHdr;
+ long count;
+
+ switch(uMsg) {
+ case WIM_OPEN:
+ break;
+ case WIM_CLOSE:
+ break;
+ case WIM_DATA:
+ hHdr = (LPWAVEHDR)dwParam1;
+ if(hHdr != NULL) {
+ count = hHdr->dwBytesRecorded;
+ if(count > 0) {
+ WaitForSingleObject(inlock, INFINITE);
+ if(hHdr->dwUser == Ping)
+ audio_ping.sz = count;
+ else
+ audio_pong.sz = count;
+ ReleaseMutex(inlock);
+ }
+ }
+ break;
+ }
+ return;
+}
+
+
+void CALLBACK
+waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwOutstance, DWORD dwParam1, DWORD dwParam2)
+{
+ LPWAVEHDR hHdr;
+
+ switch(uMsg) {
+ case WOM_DONE:
+ hHdr = (LPWAVEHDR)dwParam1;
+ if(hHdr != NULL) {
+ WaitForSingleObject(outlock, INFINITE);
+ out_buf_count -= hHdr->dwUser;
+ ReleaseMutex(outlock);
+ audioerror(
+ waveOutUnprepareHeader(
+ audio_file_out, hHdr, sizeof(WAVEHDR)),
+ AUDIOOUT, "out un prepare header");
+ if(hHdr->lpData != NULL)
+ free(hHdr->lpData);
+ free(hHdr);
+ }
+ break;
+ case WOM_CLOSE:
+ WaitForSingleObject(outlock, INFINITE);
+ out_buf_count = 0;
+ ReleaseMutex(outlock);
+ break;
+ case WOM_OPEN:
+ break;
+ }
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ WAVEFORMATEX format;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if(!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if((tmpav.in.enc != Audio_Pcm_Val) || (tmpav.out.enc != Audio_Pcm_Val))
+ error(Ebadarg);
+
+ if(tmpav.in.flags & AUDIO_MOD_FLAG) {
+ format.wFormatTag = tmpav.in.enc;
+ format.wBitsPerSample = tmpav.in.bits;
+ format.nChannels = tmpav.in.chan;
+ format.nSamplesPerSec = tmpav.in.rate;
+ format.nBlockAlign =
+ (tmpav.in.chan * tmpav.in.bits) / Bits_Per_Byte;
+ format.nAvgBytesPerSec =
+ format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ if(audioerror(
+ waveInOpen(NULL, WAVE_MAPPER, &format, 0, 0, WAVE_FORMAT_QUERY),
+ AUDIOIN, "cannot open microphone/line-in to test parameters") == -1)
+ error(Ebadarg);
+
+ qlock(&flag_lock);
+
+ if(waserror()){
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ if(audio_flags & INISOPEN) {
+ audio_close_in();
+ audio_flags &= ~INISOPEN;
+ audio_flags &= ~INPUTISGOING;
+ if(!audio_open_in(&audio_file_in, &tmpav.in))
+ error(Eio);
+ audio_flags |= INISOPEN;
+ }
+ poperror();
+ qunlock(&flag_lock);
+ }
+
+ if(tmpav.out.flags & AUDIO_MOD_FLAG) {
+
+ format.wFormatTag = tmpav.out.enc;
+ format.wBitsPerSample = tmpav.out.bits;
+ format.nChannels = tmpav.out.chan;
+ format.nSamplesPerSec = tmpav.out.rate;
+ format.nBlockAlign =
+ (tmpav.out.chan * tmpav.out.bits) / Bits_Per_Byte;
+ format.nAvgBytesPerSec =
+ format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ if (audioerror(waveOutOpen(NULL, WAVE_MAPPER,
+ &format,
+ 0, 0, WAVE_FORMAT_QUERY),
+ AUDIOOUT, "cannot open output to test parameters") == -1)
+ error(Ebadarg);
+
+ qlock(&flag_lock);
+ if(waserror()){
+ qunlock(&flag_lock);
+ nexterror();
+ }
+ if(audio_flags & OUTISOPEN) {
+ audio_close_out();
+
+ audio_flags &= ~OUTISOPEN;
+ if(!audio_open_out(&audio_file_out, &tmpav.out)) {
+ error(Eio);
+ return -1;
+ }
+ audio_flags |= OUTISOPEN;
+ }
+ poperror();
+ qunlock(&flag_lock);
+ }
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
--- /dev/null
+++ b/emu/Nt/cmd.c
@@ -1,0 +1,243 @@
+#define Unknown win_Unknown
+#define UNICODE
+#include <windows.h>
+#include <winbase.h>
+#include <winsock.h>
+#undef Unknown
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+extern int nth2fd(HANDLE);
+extern wchar_t *widen(char*);
+
+/*
+ * thanks to rcsh for these.
+ *
+ * windows quoting rules - I think
+ * Words are separated by space or tab
+ * Words containing a space or tab can be quoted using "
+ * 2N backslashes + " ==> N backslashes and end quote
+ * 2N+1 backslashes + " ==> N backslashes + literal "
+ * N backslashes not followed by " ==> N backslashes
+ */
+static char *
+dblquote(char *cmd, char *s)
+{
+ int nb;
+ char *p;
+
+ for(p=s; *p; p++)
+ if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"')
+ break;
+
+ if(*p == 0){ /* easy case */
+ strcpy(cmd, s);
+ return cmd+(p-s);
+ }
+
+ *cmd++ = '"';
+ for(;;) {
+ for(nb=0; *s=='\\'; nb++)
+ *cmd++ = *s++;
+
+ if(*s == 0) { /* trailing backslashes -> 2N */
+ while(nb-- > 0)
+ *cmd++ = '\\';
+ break;
+ }
+
+ if(*s == '"') { /* literal quote -> 2N+1 backslashes */
+ while(nb-- > 0)
+ *cmd++ = '\\';
+ *cmd++ = '\\'; /* escape the quote */
+ }
+ *cmd++ = *s++;
+ }
+
+ *cmd++ = '"';
+ *cmd = 0;
+
+ return cmd;
+}
+
+static char *
+ntquotedcmd(char **argv)
+{
+ int i, n;
+ char *cmd, *p;
+
+ /* conservatively calculate length of command;
+ * backslash expansion can cause growth in dblquote().
+ */
+ for(i=0,n=0; argv[i]; i++)
+ n += 2*strlen(argv[i]);
+ n++;
+
+ cmd = malloc(n);
+ if(cmd == nil)
+ return nil;
+ for(i=0,p=cmd; argv[i]; i++) {
+ p = dblquote(p, argv[i]);
+ *p++ = ' ';
+ }
+ if(p != cmd)
+ p--;
+ *p = 0;
+
+ return cmd;
+}
+
+static HANDLE
+exporthandle(HANDLE h, int close)
+{
+ HANDLE cp, dh;
+ DWORD flags = DUPLICATE_SAME_ACCESS;
+ if (close)
+ flags |= DUPLICATE_CLOSE_SOURCE;
+ cp = GetCurrentProcess();
+ if (!DuplicateHandle(cp, h, cp, &dh, DUPLICATE_SAME_ACCESS, 1, flags))
+ return nil;
+ return dh;
+}
+
+/* TO DO: check that oserrstr will have the right text on error */
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ STARTUPINFO si;
+ SECURITY_ATTRIBUTES sec;
+ HANDLE rh, wh, eh, srh, swh, seh;
+ PROCESS_INFORMATION pinfo;
+ char *cmd;
+ wchar_t *wcmd, *wdir;
+ int prio;
+
+ wdir = nil;
+ if(dir != nil)
+ wdir = widen(dir);
+
+ cmd = ntquotedcmd(args);
+ if(cmd == nil)
+ error(Enomem);
+
+ wcmd = widen(cmd);
+ sec.nLength = sizeof(sec);
+ sec.lpSecurityDescriptor = 0;
+ sec.bInheritHandle = 0;
+ rh = wh = eh = srh = swh = seh = nil;
+ if(!CreatePipe(&rh, &swh, &sec, 0))
+ goto Error;
+ if(!CreatePipe(&srh, &wh, &sec, 0))
+ goto Error;
+ if(!CreatePipe(&seh, &eh, &sec, 0))
+ goto Error;
+ rh = exporthandle(rh, 1);
+ if(rh == nil)
+ goto Error;
+ wh = exporthandle(wh, 1);
+ if(wh == nil)
+ goto Error;
+ eh = exporthandle(eh, 1);
+ if(eh == nil)
+ goto Error;
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
+ si.wShowWindow = SW_SHOW;
+ si.hStdInput = rh;
+ si.hStdOutput = wh;
+ si.hStdError = eh;
+
+ prio = 0;
+ if(nice){
+ prio = IDLE_PRIORITY_CLASS;
+ if(nice > 1)
+ prio |= CREATE_SUSPENDED;
+ }
+
+ /* default of nil for wpath seems to be what we want; nil for env exports our current one */
+ if(!CreateProcess(nil/*wpath*/, wcmd, 0, 0, 1,
+ CREATE_NEW_PROCESS_GROUP|CREATE_DEFAULT_ERROR_MODE|prio,
+ 0 /*env*/, wdir, &si, &pinfo)){
+ //print("can't create process '%Q' %d\n", wcmd, GetLastError());
+ goto Error;
+ }
+
+ fd[0] = nth2fd(swh);
+ fd[1] = nth2fd(srh);
+ fd[2] = nth2fd(seh);
+ if(fd[1] == 1 || fd[2] == 2)
+ panic("invalid mapping of handle to fd");
+ CloseHandle(si.hStdInput);
+ CloseHandle(si.hStdOutput);
+ CloseHandle(si.hStdError);
+
+ if(prio & CREATE_SUSPENDED){
+ if(nice > 1)
+ SetThreadPriority(pinfo.hThread,
+ nice>3? THREAD_PRIORITY_IDLE:
+ nice>2? THREAD_PRIORITY_LOWEST:
+ THREAD_PRIORITY_BELOW_NORMAL);
+ ResumeThread(pinfo.hThread);
+ }
+ CloseHandle(pinfo.hThread);
+ /* don't close process handle */
+ free(cmd);
+ free(wcmd);
+ free(wdir);
+ return pinfo.hProcess;
+
+Error:
+ if(rh)
+ CloseHandle(rh);
+ if(wh)
+ CloseHandle(wh);
+ if(eh)
+ CloseHandle(eh);
+ if(srh)
+ CloseHandle(srh);
+ if(swh)
+ CloseHandle(swh);
+ if(seh)
+ CloseHandle(seh);
+ free(cmd);
+ free(wcmd);
+ free(wdir);
+ return nil;
+}
+
+int
+oscmdwait(void *v, char *buf, int n)
+{
+ int status;
+ HANDLE proc = (HANDLE)v;
+
+ /* need not worry about being interrupted */
+ if(WaitForSingleObject(proc, INFINITE) == WAIT_FAILED)
+ return -1;
+ if(!GetExitCodeProcess(proc, &status))
+ status = 1;
+ if(status)
+ n = snprint(buf, n, "0 0 0 0 'status %d'", status);
+ else
+ n = snprint(buf, n, "0 0 0 0 ''");
+ return n;
+
+}
+
+int
+oscmdkill(void *v)
+{
+ if(TerminateProcess((HANDLE)v, 666) == FALSE)
+ return -1;
+ return 0;
+}
+
+void
+oscmdfree(void *v)
+{
+ CloseHandle((HANDLE)v);
+}
--- /dev/null
+++ b/emu/Nt/devarch.c
@@ -1,0 +1,442 @@
+/*
+ * platform-specific interface
+ */
+
+#define Unknown win_Unknown
+#define UNICODE
+#include <windows.h>
+#include <winbase.h>
+#include <winsock.h>
+#undef Unknown
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "r16.h"
+
+enum{
+ Qdir,
+ Qarchctl,
+ Qcputype,
+ Qregquery,
+ Qhostmem
+};
+
+static
+Dirtab archtab[]={
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "archctl", {Qarchctl, 0}, 0, 0444,
+ "cputype", {Qcputype}, 0, 0444,
+ "regquery", {Qregquery}, 0, 0666,
+ "hostmem", {Qhostmem}, 0, 0444,
+};
+
+typedef struct Value Value;
+struct Value {
+ int type;
+ int size;
+ union {
+ ulong w;
+ vlong q;
+ char data[1]; /* utf-8 */
+ };
+};
+
+typedef struct Regroot Regroot;
+struct Regroot {
+ char* name;
+ HKEY root;
+};
+
+static Regroot roots[] = {
+ {"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT},
+ {"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG},
+ {"HKEY_CURRENT_USER", HKEY_CURRENT_USER},
+ {"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE},
+ {"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA},
+ {"HKEY_USERS", HKEY_USERS},
+};
+
+static struct {
+ ulong mhz;
+ int ncpu;
+ char cpu[64];
+} arch;
+
+static QLock reglock;
+
+static Value* getregistry(HKEY, Rune16*, Rune16*);
+static int nprocs(void);
+
+static void
+archinit(void)
+{
+ Value *v;
+ char *p;
+
+ v = getregistry(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"ProcessorNameString");
+ if(v != nil){
+ snprint(arch.cpu, sizeof(arch.cpu), "%s", v->data);
+ if((p = strrchr(arch.cpu, ' ')) != nil)
+ for(; p >= arch.cpu && *p == ' '; p--)
+ *p = '\0';
+ free(v);
+ }else{
+ v = getregistry(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"VendorIdentifier");
+ if(v != nil){
+ snprint(arch.cpu, sizeof(arch.cpu), "%s", v->data);
+ free(v);
+ }else
+ snprint(arch.cpu, sizeof(arch.cpu), "unknown");
+ }
+ v = getregistry(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"~MHz");
+ if(v != nil){
+ arch.mhz = v->w;
+ free(v);
+ }
+ arch.ncpu = nprocs();
+}
+
+static int
+nprocs(void)
+{
+ int n;
+ char *p;
+ Rune16 *r;
+ Value *v;
+ n = 0;
+ for(;;){
+ p = smprint("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d", n);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ r = widen(p);
+ free(p);
+ v = getregistry(HKEY_LOCAL_MACHINE, r, L"~MHz");
+ free(r);
+ if(v == nil)
+ break;
+ free(v);
+ n++;
+ }
+ return n;
+}
+
+static Chan*
+archattach(char* spec)
+{
+ return devattach('a', spec);
+}
+
+static Walkqid*
+archwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, archtab, nelem(archtab), devgen);
+}
+
+static int
+archstat(Chan* c, uchar *db, int n)
+{
+ return devstat(c, db, n, archtab, nelem(archtab), devgen);
+}
+
+static Chan*
+archopen(Chan* c, int omode)
+{
+ return devopen(c, omode, archtab, nelem(archtab), devgen);
+}
+
+static void
+archclose(Chan* c)
+{
+ if((ulong)c->qid.path == Qregquery && c->aux != nil)
+ free(c->aux);
+}
+
+static long
+archread(Chan* c, void* a, long n, vlong offset)
+{
+ char *p;
+ Value *v;
+ int i, l;
+ MEMORYSTATUS mem;
+
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, a, n, archtab, nelem(archtab), devgen);
+ case Qarchctl:
+ case Qcputype:
+ l = 0;
+ if((ulong)c->qid.path == Qcputype)
+ l = 4;
+ p = smalloc(READSTR);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ snprint(p, READSTR, "cpu %q %lud %d\n", arch.cpu, arch.mhz, arch.ncpu);
+ n = readstr(offset, a, n, p+l);
+ poperror();
+ free(p);
+ break;
+ case Qregquery:
+ v = c->aux;
+ if(v == nil)
+ return 0;
+ p = smalloc(READSTR);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ switch(v->type){
+ case REG_NONE:
+ n = readstr(offset, a, n, "nil");
+ break;
+ case REG_DWORD:
+ snprint(p, READSTR, "int %ld", v->w);
+ n = readstr(offset, a, n, p);
+ break;
+#ifdef REG_QWORD
+ case REG_QWORD:
+ snprint(p, READSTR, "int %lld", v->q);
+ n = readstr(offset, a, n, p);
+ break;
+#endif
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ if(v->data[0])
+ snprint(p, READSTR, "str %q", v->data);
+ n = readstr(offset, a, n, p);
+ break;
+ case REG_MULTI_SZ:
+ l = snprint(p, READSTR, "str");
+ for(i=0;;){
+ l += snprint(p+l, READSTR-l, " %q", v->data+i);
+ while(v->data[i++] != 0){
+ /* skip */
+ }
+ if(v->data[i] == 0)
+ break; /* final terminator */
+ }
+ n = readstr(offset, a, n, p);
+ break;
+ case REG_BINARY:
+ l = n;
+ n = readstr(offset, a, l, "bin");
+ if(n >= 3){
+ offset -= 3;
+ if(offset+l > v->size)
+ l = v->size - offset;
+ memmove((char*)a+n, v->data+offset, l);
+ n += l;
+ }
+ break;
+ default:
+ error("unknown registry type");
+ n=0;
+ break;
+ }
+ poperror();
+ free(p);
+ c->aux = nil;
+ free(v);
+ break;
+ case Qhostmem:
+ mem.dwLength = sizeof(mem);
+ GlobalMemoryStatus(&mem); /* GlobalMemoryStatusEx isn't on NT */
+ p = smalloc(READSTR);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ snprint(p, READSTR, "load %ld\nphys %lud %lud\nvirt %lud %lud\nswap %lud %lud\n",
+ mem.dwMemoryLoad,
+ mem.dwAvailPhys, mem.dwTotalPhys, mem.dwAvailVirtual, mem.dwTotalVirtual,
+ mem.dwAvailPageFile, mem.dwTotalPageFile);
+ n = readstr(offset, a, n, p);
+ poperror();
+ free(p);
+ break;
+ default:
+ n=0;
+ break;
+ }
+ return n;
+}
+
+static long
+archwrite(Chan* c, void* a, long n, vlong offset)
+{
+ Value *v;
+ int i;
+ Cmdbuf *cb;
+ Rune16 *key, *item;
+
+ if((ulong)c->qid.path != Qregquery)
+ error(Eperm);
+ USED(offset);
+ if(c->aux != nil){
+ free(c->aux);
+ c->aux = nil;
+ }
+ cb = parsecmd(a, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ if(cb->nf < 3)
+ error(Ebadctl);
+ for(i=0; i<nelem(roots); i++)
+ if(strcmp(cb->f[0], roots[i].name) == 0)
+ break;
+ if(i >= nelem(roots))
+ errorf("unknown root: %s", cb->f[0]);
+ key = widen(cb->f[1]);
+ if(waserror()){
+ free(key);
+ nexterror();
+ }
+ item = widen(cb->f[2]);
+ if(waserror()){
+ free(item);
+ nexterror();
+ }
+ v = getregistry(roots[i].root, key, item);
+ if(v == nil)
+ error(up->env->errstr);
+ c->aux = v;
+ poperror();
+ free(item);
+ poperror();
+ free(key);
+ poperror();
+ free(cb);
+ return n;
+}
+
+Dev archdevtab = {
+ 'a',
+ "arch",
+
+ archinit,
+ archattach,
+ archwalk,
+ archstat,
+ archopen,
+ devcreate,
+ archclose,
+ archread,
+ devbread,
+ archwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
+
+static void
+regerr(int rc)
+{
+ Rune16 err[64];
+ char emsg[sizeof(err)*UTFmax+1];
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ err, sizeof(err), 0);
+ runes16toutf(emsg, err, nelem(err));
+ error(emsg);
+}
+
+static Value*
+getregistry(HKEY root, Rune16 *keyname, Rune16 *name)
+{
+ long res;
+ HKEY key;
+ DWORD dtype, n;
+ int i, l, nb;
+ void* vp;
+ char *p;
+ Value *val;
+ Rune16 *rb;
+
+ qlock(®lock);
+ if(waserror()){
+ qunlock(®lock);
+ return nil;
+ }
+ res = RegOpenKey(root, keyname, &key);
+ if(res != ERROR_SUCCESS)
+ regerr(res);
+ if(waserror()){
+ RegCloseKey(key);
+ nexterror();
+ }
+ n = 0;
+ res = RegQueryValueEx(key, name, NULL, &dtype, NULL, &n);
+ if(res != ERROR_SUCCESS)
+ regerr(res);
+ nb = n;
+ if(dtype == REG_SZ || dtype == REG_EXPAND_SZ || dtype == REG_MULTI_SZ){
+ nb = n*UTFmax + 1;
+ rb = smalloc((n+2)*sizeof(Rune16));
+ memset(rb, 0, (n+2)*sizeof(Rune16));
+ }else
+ rb = nil;
+ if(waserror()){
+ free(rb);
+ nexterror();
+ }
+ val = smalloc(sizeof(Value)+nb);
+ if(waserror()){
+ free(val);
+ nexterror();
+ }
+ val->type = dtype;
+ val->size = n;
+ switch(dtype){
+ case REG_DWORD:
+ vp = &val->w;
+ break;
+#ifdef REG_QWORD
+ case REG_QWORD:
+ vp = &val->q;
+ break;
+#endif
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ case REG_MULTI_SZ:
+ vp = rb;
+ break;
+ case REG_BINARY:
+ case REG_NONE:
+ vp = val->data;
+ break;
+ default:
+ errorf("unsupported registry type: %d", dtype);
+ return nil; /* for compiler */
+ }
+ res = RegQueryValueEx(key, name, NULL, NULL, vp, &n);
+ if(res != ERROR_SUCCESS)
+ regerr(res);
+ poperror();
+ if(rb != nil){
+ if(dtype == REG_MULTI_SZ){
+ p = val->data;
+ for(i=0;;){
+ l = runes16len(rb+i);
+ runes16toutf(p, rb+i, l);
+ i += l+1;
+ if(rb[i] == 0)
+ break;
+ p += strlen(p)+1;
+ }
+ }else
+ runes16toutf(val->data, rb, n);
+ free(rb);
+ }
+ poperror();
+ poperror();
+ RegCloseKey(key);
+ poperror();
+ qunlock(®lock);
+ return val;
+}
--- /dev/null
+++ b/emu/Nt/deveia.c
@@ -1,0 +1,672 @@
+/*
+ * Windows serial driver
+ *
+ * to do:
+ * scan the registry for serial ports?
+ */
+
+#define Unknown win_Unknown
+#include <windows.h>
+#undef Unknown
+#undef Sleep
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <lm.h>
+#include <direct.h>
+
+// local fcts
+static void openport(int);
+static void wrctl(int, char*);
+static long rdstat(int, void*, long, ulong );
+
+enum
+{
+ Devchar = 't',
+
+ Ndataqid = 1,
+ Nctlqid,
+ Nstatqid,
+ Nqid = 3, /* number of QIDs */
+
+ Maxctl = 128,
+
+ // in/out buffer sizes for comm port (NT requires an even number)
+ // set it to x* the max styx message rounded up to the
+ // nearest 4 byte value
+ CommBufSize = ((((8192+128)*2)+3) & ~3)
+};
+
+/*
+ * Macros to manage QIDs
+ */
+#define NETTYPE(x) ((x)&0x0F)
+#define NETID(x) ((x)>>4)
+#define NETQID(i,t) (((i)<<4)|(t))
+
+static Dirtab *eiadir;
+static int ndir;
+
+typedef struct Eia Eia;
+struct Eia {
+ Ref r;
+ HANDLE comfh; //handle to open port
+ int restore; //flag to restore prev. states
+ DCB dcb; //win32 device control block used for restore
+ int id; //index to host port name in sysdev
+};
+
+// the same timeouts are used for all ports
+// currently there is no Inferno interface to
+// change the timeouts.
+static COMMTIMEOUTS timeouts;
+
+// std win32 serial port names are COM1..COM4
+// however there can be more and they can be
+// named anything. we should be more flexible
+// pehaps adding a ctl command to allow you to
+// access any win32 comm port
+static char* sysdev[] = {
+ "COM1:",
+ "COM2:",
+ "COM3:",
+ "COM4:",
+ "COM5:",
+ "COM6:",
+ "COM7:",
+ "COM8:",
+ NULL
+};
+
+static Eia *eia;
+
+typedef struct OptTable OptTable;
+struct OptTable {
+ char *str;
+ DWORD flag;
+};
+
+#define BAD ((DWORD)-1)
+
+// valid bit-per-byte sizes
+static OptTable size[] = {
+ {"5", 5},
+ {"6", 6},
+ {"7", 7},
+ {"8", 8},
+ {NULL, BAD}
+};
+
+// valid stop bits
+static OptTable stopbits[] = {
+ {"1", ONESTOPBIT},
+ {"1.5", ONE5STOPBITS},
+ {"2", TWOSTOPBITS},
+ {NULL, BAD}
+};
+
+// valid parity settings
+static OptTable parity[] = {
+ {"o", ODDPARITY},
+ {"e", EVENPARITY},
+ {"s", SPACEPARITY},
+ {"m", MARKPARITY},
+ {"n", NOPARITY},
+ {NULL, NOPARITY}
+};
+
+
+static char *
+ftos(OptTable *tbl, DWORD flag)
+{
+ while(tbl->str && tbl->flag != flag)
+ tbl++;
+ if(tbl->str == 0)
+ return "unknown";
+ return tbl->str;
+}
+
+static DWORD
+stof(OptTable *tbl, char *str)
+{
+ while(tbl->str && strcmp(tbl->str, str) != 0)
+ tbl++;
+ return tbl->flag;
+}
+
+static void
+eiainit(void)
+{
+ int i,x;
+ byte ports; //bitmask of active host ports
+ int nports; //number of active host ports
+ int max; //number of highest port
+ Dirtab *dp;
+
+ // setup the timeouts; choose carefully
+ timeouts.ReadIntervalTimeout = 2;
+ timeouts.ReadTotalTimeoutMultiplier = 0;
+ timeouts.ReadTotalTimeoutConstant = 200;
+ timeouts.WriteTotalTimeoutMultiplier = 0;
+ timeouts.WriteTotalTimeoutConstant = 400;
+
+ // check to see which ports exist by trying to open them
+ // keep results in a bitmask
+ ports = nports = max = 0;
+ for(i=0; (sysdev[i] != NULL) && (i<8); i++) {
+ HANDLE comfh = CreateFile(sysdev[i], 0, 0, NULL, /* no security attrs */
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if(comfh != INVALID_HANDLE_VALUE) {
+ ports |= 1<<i;
+ CloseHandle(comfh);
+ nports++;
+ max = i;
+ }
+ }
+
+ if(nports == 0)
+ return; //no ports
+
+ // allocate directory table and eia structure
+ // for each active port.
+ ndir = Nqid*nports+1;
+ dp = eiadir = malloc(ndir*sizeof(Dirtab));
+ if(dp == 0)
+ panic("eiainit");
+ eia = malloc(nports*sizeof(Eia));
+ if(eia == 0) {
+ free(dp);
+ panic("eiainit");
+ }
+
+ // fill in the directory table and initialize
+ // the eia structure. skip inactive ports.
+ sprint(dp->name, ".");
+ dp->qid.path = 0;
+ dp->qid.type = QTDIR;
+ dp->perm = DMDIR|0555;
+ dp++;
+ x = 0; // index in eia[]
+ for(i = 0; i <= max; i++) {
+ if( (ports & (1<<i)) == 0)
+ continue; //port 'i' is not active
+ sprint(dp->name, "eia%d", i);
+ dp->qid.path = NETQID(x, Ndataqid);
+ dp->perm = 0660;
+ dp++;
+ sprint(dp->name, "eia%dctl", i);
+ dp->qid.path = NETQID(x, Nctlqid);
+ dp->perm = 0660;
+ dp++;
+ sprint(dp->name, "eia%dstatus", i);
+ dp->qid.path = NETQID(x, Nstatqid);
+ dp->perm = 0660;
+ dp++;
+ // init the eia structure
+ eia[x].restore = 0;
+ eia[x].id = i;
+ x++;
+ }
+}
+
+static Chan*
+eiaattach(char *spec)
+{
+ if(eiadir == nil)
+ error(Enodev);
+
+ return devattach(Devchar, spec);
+}
+
+static Walkqid*
+eiawalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, eiadir, ndir, devgen);
+}
+
+static int
+eiastat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, eiadir, ndir, devgen);
+}
+
+static Chan*
+eiaopen(Chan *c, int mode)
+{
+ int port = NETID(c->qid.path);
+
+ c = devopen(c, mode, eiadir, ndir, devgen);
+
+ switch(NETTYPE(c->qid.path)) {
+ case Nctlqid:
+ case Ndataqid:
+ case Nstatqid:
+ if(incref(&eia[port].r) != 1)
+ break;
+ if(waserror()) {
+ decref(&eia[port].r);
+ nexterror();
+ }
+ openport(port);
+ poperror();
+ break;
+ }
+ return c;
+}
+
+static void
+eiaclose(Chan *c)
+{
+ int port = NETID(c->qid.path);
+
+ if((c->flag & COPEN) == 0)
+ return;
+
+ switch(NETTYPE(c->qid.path)) {
+ case Nctlqid:
+ case Ndataqid:
+ case Nstatqid:
+ if(decref(&eia[port].r) == 0) {
+ osenter();
+ CloseHandle(eia[port].comfh);
+ osleave();
+ }
+ break;
+ }
+
+}
+
+static long
+eiaread(Chan *c, void *buf, long n, vlong offset)
+{
+ DWORD cnt;
+ int port = NETID(c->qid.path);
+ BOOL good;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, eiadir, ndir, devgen);
+
+ switch(NETTYPE(c->qid.path)) {
+ case Ndataqid:
+ cnt = 0;
+ // if ReadFile timeouts and cnt==0 then just re-read
+ // this will give osleave() a chance to detect an
+ // interruption (i.e. killprog)
+ while(cnt==0) {
+ osenter();
+ good = ReadFile(eia[port].comfh, buf, n, &cnt, NULL);
+ SleepEx(0,FALSE); //allow another thread access to port
+ osleave();
+ if(!good)
+ oserror();
+ }
+ return cnt;
+ case Nctlqid:
+ return readnum(offset, buf, n, eia[port].id, NUMSIZE);
+ case Nstatqid:
+ return rdstat(port, buf, n, offset);
+ }
+
+ return 0;
+}
+
+static long
+eiawrite(Chan *c, void *buf, long n, vlong offset)
+{
+ DWORD cnt;
+ char cmd[Maxctl];
+ int port = NETID(c->qid.path);
+ BOOL good;
+ uchar *data;
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ switch(NETTYPE(c->qid.path)) {
+ case Ndataqid:
+ cnt = 0;
+ data = (uchar*)buf;
+ // if WriteFile times out (i.e. return true; cnt<n) then
+ // allow osleave() to check for an interrupt otherwise try
+ // to send the unsent data.
+ while(n>0) {
+ osenter();
+ good = WriteFile(eia[port].comfh, data, n, &cnt, NULL);
+ osleave();
+ if(!good)
+ oserror();
+ data += cnt;
+ n -= cnt;
+ }
+ return (data-(uchar*)buf);
+ case Nctlqid:
+ if(n >= sizeof(cmd))
+ n = sizeof(cmd)-1;
+ memmove(cmd, buf, n);
+ cmd[n] = 0;
+ wrctl(port, cmd);
+ return n;
+ }
+ return 0;
+}
+
+static int
+eiawstat(Chan *c, uchar *dp, int n)
+{
+ Dir d;
+ int i;
+
+ if(!iseve())
+ error(Eperm);
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+ if(NETTYPE(c->qid.path) == Nstatqid)
+ error(Eperm);
+
+ n = convM2D(dp, n, &d, nil);
+ i = Nqid*NETID(c->qid.path)+NETTYPE(c->qid.path)-Ndataqid;
+ if(d.mode != ~0UL)
+ eiadir[i+1].perm = d.mode&0666;
+ return n;
+}
+
+Dev eiadevtab = {
+ Devchar,
+ "eia",
+
+ eiainit,
+ eiaattach,
+ eiawalk,
+ eiastat,
+ eiaopen,
+ devcreate,
+ eiaclose,
+ eiaread,
+ devbread,
+ eiawrite,
+ devbwrite,
+ devremove,
+ eiawstat
+};
+
+
+//
+// local functions
+//
+
+/*
+ * open the indicated comm port and then set
+ * the default settings for the port.
+ */
+static void
+openport(int port)
+{
+ Eia* p = &eia[port];
+
+ // open the port
+ p->comfh = CreateFile(sysdev[p->id],
+ GENERIC_READ|GENERIC_WRITE, //open underlying port for rd/wr
+ 0, //comm port can't be shared
+ NULL, //no security attrs
+ OPEN_EXISTING, //a must for comm port
+ FILE_ATTRIBUTE_NORMAL, //nonoverlapped io
+ NULL); //another must for comm port
+
+ if(p->comfh == INVALID_HANDLE_VALUE)
+ oserror();
+ if(waserror()){
+ CloseHandle(p->comfh);
+ p->comfh = INVALID_HANDLE_VALUE;
+ nexterror();
+ }
+
+ // setup in/out buffers (NT requires an even number)
+ if(!SetupComm(p->comfh, CommBufSize, CommBufSize))
+ oserror();
+
+ // either use existing settings or set defaults
+ if(!p->restore) {
+ // set default settings
+ if(!GetCommState(p->comfh, &p->dcb))
+ oserror();
+ p->dcb.BaudRate = 9600;
+ p->dcb.ByteSize = 8;
+ p->dcb.fParity = 0;
+ p->dcb.Parity = NOPARITY;
+ p->dcb.StopBits = ONESTOPBIT;
+ p->dcb.fInX = 0; //default to xoff
+ p->dcb.fOutX = 0;
+ p->dcb.fAbortOnError = 1; //read/write abort on err
+ }
+
+ // set state and timeouts
+ if(!SetCommState(p->comfh, &p->dcb) ||
+ !SetCommTimeouts(p->comfh, &timeouts))
+ oserror();
+ poperror();
+}
+
+/*
+ * Obtain status information on the com port.
+ */
+static long
+rdstat(int port, void *buf, long n, ulong offset)
+{
+ HANDLE comfh = eia[port].comfh;
+ char str[Maxctl];
+ char *s;
+ DCB dcb;
+ DWORD modemstatus;
+ DWORD porterr;
+ COMSTAT portstat;
+ int frame, overrun, i;
+
+ // valid line control ids
+ static enum {
+ L_CTS, L_DSR, L_RING, L_DCD, L_DTR, L_RTS, L_MAX
+ };
+ int status[L_MAX];
+
+ // line control strings (should match above id's)
+ static char* lines[] = {
+ "cts", "dsr", "ring", "dcd", "dtr", "rts", NULL
+ };
+
+
+ // get any error conditions; also clears error flag
+ // and enables io
+ if(!ClearCommError(comfh, &porterr, &portstat))
+ oserror();
+
+ // get comm port state
+ if(!GetCommState(comfh, &dcb))
+ oserror();
+
+ // get modem line information
+ if(!GetCommModemStatus(comfh, &modemstatus))
+ oserror();
+
+ // now set our local flags
+ status[L_CTS] = MS_CTS_ON & modemstatus;
+ status[L_DSR] = MS_DSR_ON & modemstatus;
+ status[L_RING] = MS_RING_ON & modemstatus;
+ status[L_DCD] = MS_RLSD_ON & modemstatus;
+ status[L_DTR] = FALSE; //?? cand this work: dcb.fDtrControl;
+ status[L_RTS] = FALSE; //?? dcb.fRtsControl;
+ frame = porterr & CE_FRAME;
+ overrun = porterr & CE_OVERRUN;
+
+ /* TO DO: mimic native eia driver's first line */
+
+ s = seprint(str, str+sizeof(str), "opens %d ferr %d oerr %d baud %d",
+ eia[port].r.ref-1,
+ frame,
+ overrun,
+ dcb.BaudRate);
+
+ // add line settings
+ for(i=0; i < L_MAX; i++)
+ if(status[i])
+ s = seprint(s, str+sizeof(str), " %s", lines[i]);
+ seprint(s, str+sizeof(str), "\n");
+ return readstr(offset, buf, n, str);
+}
+
+//
+// write on ctl file. modify the settings for
+// the underlying port.
+//
+static void
+wrctl(int port, char *cmd)
+{
+ DCB dcb;
+ int nf, n, i;
+ char *f[16];
+ HANDLE comfh = eia[port].comfh;
+ DWORD flag, opt;
+ BOOL rslt;
+ int chg;
+
+ // get the current settings for the port
+ if(!GetCommState(comfh, &dcb))
+ oserror();
+
+ chg = 0;
+ nf = tokenize(cmd, f, nelem(f));
+ for(i = 0; i < nf; i++){
+ if(strcmp(f[i], "break") == 0){
+ if(!SetCommBreak(comfh))
+ oserror();
+ SleepEx((DWORD)300, FALSE);
+ if(!ClearCommBreak(comfh))
+ oserror();
+ continue;
+ }
+
+ n = atoi(f[i]+1);
+ switch(*f[i]) {
+ case 'B':
+ case 'b': // set the baud rate
+ if(n < 110)
+ error(Ebadarg);
+ dcb.BaudRate = n;
+ chg = 1;
+ break;
+ case 'C':
+ case 'c':
+ /* dcd */
+ break;
+ case 'D':
+ case 'd': // set DTR
+ opt = n ? SETDTR : CLRDTR;
+ if(!EscapeCommFunction(comfh, opt))
+ oserror();
+ break;
+ case 'E':
+ case 'e':
+ /* dsr */
+ break;
+ case 'F':
+ case 'f': // flush any untransmitted data
+ if(!PurgeComm(comfh, PURGE_TXCLEAR))
+ oserror();
+ break;
+ case 'H':
+ case 'h':
+ /* hangup */
+ /* TO DO: close handle */
+ break;
+ case 'I':
+ case 'i':
+ /* fifo: nothing to do */
+ break;
+ case 'K':
+ case 'k':
+ /* send a break */
+ if(!SetCommBreak(comfh))
+ oserror();
+ SleepEx((DWORD)300, FALSE);
+ if(!ClearCommBreak(comfh))
+ oserror();
+ break;
+ case 'L':
+ case 'l': // set bits per byte
+ flag = stof(size, f[0]+1);
+ if(flag == BAD)
+ error(Ebadarg);
+ dcb.ByteSize = (BYTE)flag;
+ chg = 1;
+ break;
+ case 'M':
+ case 'm': // set CTS (modem control)
+ dcb.fOutxCtsFlow = (n!=0);
+ chg = 1;
+ break;
+ case 'N':
+ case 'n':
+ /* don't block on output */
+ break;
+ case 'P':
+ case 'p': // set parity -- even or odd
+ flag = stof(parity, f[0]+1);
+ if(flag==BAD)
+ error(Ebadarg);
+ dcb.Parity = (BYTE)flag;
+ chg = 1;
+ break;
+ case 'Q':
+ case 'q':
+ /* set i/o queue limits */
+ break;
+ case 'R':
+ case 'r': // set RTS
+ opt = n ? SETRTS : CLRRTS;
+ if(!EscapeCommFunction(comfh, opt))
+ oserror();
+ break;
+ case 'S':
+ case 's': // set stop bits -- valid: 1 or 2 (win32 allows 1.5??)
+ flag = stof(stopbits, f[0]+1);
+ if(flag==BAD)
+ error(Ebadarg);
+ dcb.StopBits = flag;
+ chg = 1;
+ break;
+ case 'T':
+ case 't':
+ break;
+ case 'W':
+ case 'w':
+ /* set uart timer */
+ break;
+ case 'X':
+ case 'x': // xon/xoff
+ opt = n ? SETXON : SETXOFF;
+ if(!EscapeCommFunction(comfh, opt))
+ oserror();
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ }
+
+ if(!chg)
+ return;
+ // make the changes on the underlying port, but flush
+ // outgoing chars down the port before
+ osenter();
+ rslt = FlushFileBuffers(comfh);
+ if(rslt)
+ rslt = SetCommState(comfh, &dcb);
+ osleave();
+ if(!rslt)
+ oserror();
+ eia[port].restore = 1;
+ eia[port].dcb = dcb;
+}
--- /dev/null
+++ b/emu/Nt/devfs.c
@@ -1,0 +1,2375 @@
+#define UNICODE
+#define Unknown win_Unknown
+#include <windows.h>
+#include <winbase.h>
+#undef Unknown
+#undef Sleep
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "r16.h"
+#include <lm.h>
+
+/* TODO: try using / in place of \ in path names */
+
+#ifndef SID_MAX_SUB_AUTHORITIES
+#define SID_MAX_SUB_AUTHORITIES 15
+#endif
+
+enum
+{
+ MAX_SID = sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(DWORD),
+ ACL_ROCK = sizeof(ACL) + 20*(sizeof(ACCESS_ALLOWED_ACE)+MAX_SID),
+ SD_ROCK = SECURITY_DESCRIPTOR_MIN_LENGTH + MAX_SID + ACL_ROCK,
+ MAXCOMP = 128,
+};
+
+typedef struct User User;
+typedef struct Gmem Gmem;
+typedef struct Stat Stat;
+typedef struct Fsinfo Fsinfo;
+typedef WIN32_FIND_DATA Fsdir;
+
+#ifndef INVALID_SET_FILE_POINTER
+#define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+
+struct Fsinfo
+{
+ int uid;
+ int gid;
+ int mode;
+ int fd;
+ vlong offset;
+ QLock oq;
+ char* spec;
+ Rune16* srv;
+ Cname* name; /* Windows' idea of the file name */
+ ushort usesec;
+ ushort checksec;
+ Fsdir* de; /* non-nil for saved entry from last dirread at offset */
+};
+#define FS(c) ((Fsinfo*)(c)->aux)
+
+/*
+ * info about a user or group
+ * there are two ways to specify a user:
+ * by sid, a unique identifier
+ * by user and domain names
+ * this structure is used to convert between the two,
+ * as well as figure out which groups a users belongs to.
+ * the user information never gets thrown away,
+ * but the group information gets refreshed with each setid.
+ */
+struct User
+{
+ QLock lk; /* locks the gotgroup and group fields */
+ SID *sid;
+ Rune16 *name;
+ Rune16 *dom;
+ int type; /* the type of sid, ie SidTypeUser, SidTypeAlias, ... */
+ int gotgroup; /* tried to add group */
+ Gmem *group; /* global and local groups to which this user or group belongs. */
+ User *next;
+};
+
+struct Gmem
+{
+ User *user;
+ Gmem *next;
+};
+
+/*
+ * intermediate stat information
+ */
+struct Stat
+{
+ User *owner;
+ User *group;
+ ulong mode;
+};
+
+/*
+ * some "well-known" sids
+ */
+static SID *creatorowner;
+static SID *creatorgroup;
+static SID *everyone;
+static SID *ntignore;
+static SID *ntroot; /* user who is supposed to run emu as a server */
+
+/*
+ * all users we ever see end up in this table
+ * users are never deleted, but we should update
+ * group information for users sometime
+ */
+static struct
+{
+ QLock lk;
+ User *u;
+}users;
+
+/*
+ * conversion from inferno permission modes to nt access masks
+ * is this good enough? this is what nt sets, except for NOMODE
+ */
+#define NOMODE (READ_CONTROL|FILE_READ_EA|FILE_READ_ATTRIBUTES)
+#define RMODE (READ_CONTROL|SYNCHRONIZE\
+ |FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
+#define XMODE (READ_CONTROL|SYNCHRONIZE\
+ |FILE_EXECUTE|FILE_READ_ATTRIBUTES)
+#define WMODE (DELETE|READ_CONTROL|SYNCHRONIZE|WRITE_DAC|WRITE_OWNER\
+ |FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA\
+ |FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES)
+
+static int
+modetomask[] =
+{
+ NOMODE,
+ XMODE,
+ WMODE,
+ WMODE|XMODE,
+ RMODE,
+ RMODE|XMODE,
+ RMODE|WMODE,
+ RMODE|WMODE|XMODE,
+};
+
+extern DWORD PlatformId;
+ char rootdir[MAXROOT] = "\\inferno";
+ Rune16 rootname[] = L"inferno-server";
+static Qid rootqid;
+static User *fsnone;
+static User *fsuser;
+static Rune16 *ntsrv;
+static int usesec;
+static int checksec;
+static int isserver;
+static int file_share_delete;
+static uchar isntfrog[256];
+
+static void fsremove(Chan*);
+
+ wchar_t *widen(char *s);
+ char *narrowen(wchar_t *ws);
+ int widebytes(wchar_t *ws);
+
+static char Etoolong[] = "file name too long";
+
+extern int nth2fd(HANDLE);
+extern HANDLE ntfd2h(int);
+static int cnisroot(Cname*);
+static int fsisroot(Chan*);
+static int okelem(char*, int);
+static int fsexist(char*, Qid*);
+static char* fspath(Cname*, char*, char*, char*);
+static Cname* fswalkpath(Cname*, char*, int);
+static char* fslastelem(Cname*);
+static long fsdirread(Chan*, uchar*, int, vlong);
+static ulong fsqidpath(char*);
+static int fsomode(int);
+static int fsdirset(char*, int, WIN32_FIND_DATA*, char*, Chan*, int isdir);
+static int fsdirsize(WIN32_FIND_DATA*, char*, Chan*);
+static void fssettime(char*, long, long);
+static long unixtime(FILETIME);
+static FILETIME wintime(ulong);
+static void secinit(void);
+static int secstat(Dir*, char*, Rune16*);
+static int secsize(char*, Rune16*);
+static void seccheck(char*, ulong, Rune16*);
+static int sechasperm(char*, ulong, Rune16*);
+static SECURITY_DESCRIPTOR* secsd(char*, char[SD_ROCK]);
+static int secsdhasperm(SECURITY_DESCRIPTOR*, ulong, Rune16*);
+static int secsdstat(SECURITY_DESCRIPTOR*, Stat*, Rune16*);
+static SECURITY_DESCRIPTOR* secmksd(char[SD_ROCK], Stat*, ACL*, int);
+static SID *dupsid(SID*);
+static int ismembersid(Rune16*, User*, SID*);
+static int ismember(User*, User*);
+static User *sidtouser(Rune16*, SID*);
+static User *domnametouser(Rune16*, Rune16*, Rune16*);
+static User *nametouser(Rune16*, Rune16*);
+static User *unametouser(Rune16*, char*);
+static void addgroups(User*, int);
+static User *mkuser(SID*, int, Rune16*, Rune16*);
+static Rune16 *domsrv(Rune16 *, Rune16[MAX_PATH]);
+static Rune16 *filesrv(char*);
+static int fsacls(char*);
+static User *secuser(void);
+
+
+int
+winfilematch(char *path, WIN32_FIND_DATA *data)
+{
+ char *p;
+ wchar_t *wpath;
+ int r;
+
+ p = path+strlen(path);
+ while(p > path && p[-1] != '\\')
+ --p;
+ wpath = widen(p);
+ r = (data->cFileName[0] == '.' && runes16len(data->cFileName) == 1)
+ || runes16cmp(data->cFileName, wpath) == 0;
+ free(wpath);
+ return r;
+}
+
+int
+winfileclash(char *path)
+{
+ HANDLE h;
+ WIN32_FIND_DATA data;
+ wchar_t *wpath;
+
+ wpath = widen(path);
+ h = FindFirstFile(wpath, &data);
+ free(wpath);
+ if (h != INVALID_HANDLE_VALUE) {
+ FindClose(h);
+ return !winfilematch(path, &data);
+ }
+ return 0;
+}
+
+
+/*
+ * this gets called to set up the environment when we switch users
+ */
+void
+setid(char *name, int owner)
+{
+ User *u;
+
+ if(owner && !iseve())
+ return;
+
+ kstrdup(&up->env->user, name);
+
+ if(!usesec)
+ return;
+
+ u = unametouser(ntsrv, up->env->user);
+ if(u == nil)
+ u = fsnone;
+ else {
+ qlock(&u->lk);
+ addgroups(u, 1);
+ qunlock(&u->lk);
+ }
+ if(u == nil)
+ panic("setid: user nil\n");
+
+ up->env->ui = u;
+}
+
+static void
+fsfree(Chan *c)
+{
+ cnameclose(FS(c)->name);
+ if(FS(c)->de != nil)
+ free(FS(c)->de);
+ free(FS(c));
+}
+
+void
+fsinit(void)
+{
+ int n, isvol;
+ ulong attr;
+ char *p, tmp[MAXROOT];
+ wchar_t *wp, *wpath, *last;
+ wchar_t wrootdir[MAXROOT];
+
+ isntfrog['/'] = 1;
+ isntfrog['\\'] = 1;
+ isntfrog[':'] = 1;
+ isntfrog['*'] = 1;
+ isntfrog['?'] = 1;
+ isntfrog['"'] = 1;
+ isntfrog['<'] = 1;
+ isntfrog['>'] = 1;
+
+ /*
+ * vet the root
+ */
+ strcpy(tmp, rootdir);
+ for(p = tmp; *p; p++)
+ if(*p == '/')
+ *p = '\\';
+ if(tmp[0] != 0 && tmp[1] == ':') {
+ if(tmp[2] == 0) {
+ tmp[2] = '\\';
+ tmp[3] = 0;
+ }
+ else if(tmp[2] != '\\') {
+ /* don't allow c:foo - only c:\foo */
+ panic("illegal root pathX");
+ }
+ }
+ wrootdir[0] = '\0';
+ wpath = widen(tmp);
+ for(wp = wpath; *wp; wp++) {
+ if(*wp < 32 || (*wp < 256 && isntfrog[*wp] && *wp != '\\' && *wp != ':'))
+ panic("illegal root path");
+ }
+ n = GetFullPathName(wpath, MAXROOT, wrootdir, &last);
+ free(wpath);
+ runes16toutf(rootdir, wrootdir, MAXROOT);
+ if(n >= MAXROOT || n == 0)
+ panic("illegal root path");
+
+ /* get rid of trailing \ */
+ while(rootdir[n-1] == '\\') {
+ if(n <= 2) {
+ panic("illegal root path");
+ }
+ rootdir[--n] = '\0';
+ }
+
+ isvol = 0;
+ if(rootdir[1] == ':' && rootdir[2] == '\0')
+ isvol = 1;
+ else if(rootdir[0] == '\\' && rootdir[1] == '\\') {
+ p = strchr(&rootdir[2], '\\');
+ if(p == nil)
+ panic("inferno root can't be a server");
+ isvol = strchr(p+1, '\\') == nil;
+ }
+
+ if(strchr(rootdir, '\\') == nil)
+ strcat(rootdir, "\\.");
+ attr = GetFileAttributes(wrootdir);
+ if(attr == 0xFFFFFFFF)
+ panic("root path '%s' does not exist", narrowen(wrootdir));
+ rootqid.path = fsqidpath(rootdir);
+ if(attr & FILE_ATTRIBUTE_DIRECTORY)
+ rootqid.type |= QTDIR;
+ rootdir[n] = '\0';
+
+ rootqid.vers = time(0);
+
+ /*
+ * set up for nt file security checking
+ */
+ ntsrv = filesrv(rootdir);
+ usesec = PlatformId == VER_PLATFORM_WIN32_NT; /* true for NT and 2000 */
+ if(usesec){
+ file_share_delete = FILE_SHARE_DELETE; /* sensible handling of shared files by delete and rename */
+ secinit();
+ if(!fsacls(rootdir))
+ usesec = 0;
+ }
+ checksec = usesec && isserver;
+}
+
+Chan*
+fsattach(char *spec)
+{
+ Chan *c;
+ static int devno;
+ static Lock l;
+ char *drive = (char *)spec;
+
+ if (!emptystr(drive) && (drive[1] != ':' || drive[2] != '\0'))
+ error(Ebadspec);
+
+ c = devattach('U', spec);
+ lock(&l);
+ c->dev = devno++;
+ unlock(&l);
+ c->qid = rootqid;
+ c->aux = smalloc(sizeof(Fsinfo));
+ FS(c)->srv = ntsrv;
+ if(!emptystr(spec)) {
+ char *s = smalloc(strlen(spec)+1);
+ strcpy(s, spec);
+ FS(c)->spec = s;
+ FS(c)->srv = filesrv(spec);
+ if(usesec)
+ FS(c)->usesec = fsacls(spec);
+ FS(c)->checksec = FS(c)->usesec && isserver;
+ c->qid.path = fsqidpath(spec);
+ c->qid.type = QTDIR;
+ c->qid.vers = 0;
+ }else{
+ FS(c)->usesec = usesec;
+ FS(c)->checksec = checksec;
+ }
+ FS(c)->name = newcname("/");
+ return c;
+}
+
+Walkqid*
+fswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ int j, alloc;
+ Walkqid *wq;
+ char path[MAX_PATH], *p;
+ Cname *ph;
+ Cname *current, *next;
+
+ if(nname > 0)
+ isdir(c);
+
+ alloc = 0;
+ current = nil;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc && wq->clone != nil)
+ cclose(wq->clone);
+ cnameclose(current);
+ free(wq);
+ return nil;
+ }
+ if(nc == nil){
+ nc = devclone(c);
+ nc->type = 0;
+ alloc = 1;
+ }
+ wq->clone = nc;
+ current = FS(c)->name;
+ if(current != nil)
+ incref(¤t->r);
+ for(j = 0; j < nname; j++){
+ if(!(nc->qid.type&QTDIR)){
+ if(j==0)
+ error(Enotdir);
+ break;
+ }
+ if(!okelem(name[j], 0)){
+ if(j == 0)
+ error(Efilename);
+ break;
+ }
+ p = fspath(current, name[j], path, FS(c)->spec);
+ if(FS(c)->checksec) {
+ *p = '\0';
+ if(!sechasperm(path, XMODE, FS(c)->srv)){
+ if(j == 0)
+ error(Eperm);
+ break;
+ }
+ *p = '\\';
+ }
+
+ if(strcmp(name[j], "..") == 0) {
+ if(fsisroot(c))
+ nc->qid = rootqid;
+ else{
+ ph = fswalkpath(current, "..", 1);
+ if(cnisroot(ph)){
+ nc->qid = rootqid;
+ current = ph;
+ if(current != nil)
+ incref(¤t->r);
+ }
+ else {
+ fspath(ph, 0, path, FS(c)->spec);
+ if(!fsexist(path, &nc->qid)){
+ cnameclose(ph);
+ if(j == 0)
+ error(Enonexist);
+ break;
+ }
+ }
+ next = fswalkpath(current, name[j], 1);
+ cnameclose(current);
+ current = next;
+ cnameclose(ph);
+ }
+ }
+ else{
+ if(!fsexist(path, &nc->qid)){
+ if(j == 0)
+ error(Enonexist);
+ break;
+ }
+ next = fswalkpath(current, name[j], 1);
+ cnameclose(current);
+ current = next;
+ }
+ wq->qid[wq->nqid++] = nc->qid;
+ }
+ poperror();
+ if(wq->nqid < nname){
+ cnameclose(current);
+ if(alloc)
+ cclose(wq->clone);
+ wq->clone = nil;
+ }else if(wq->clone){
+ nc->aux = smalloc(sizeof(Fsinfo));
+ nc->type = c->type;
+ FS(nc)->spec = FS(c)->spec;
+ FS(nc)->srv = FS(c)->srv;
+ FS(nc)->name = current;
+ FS(nc)->usesec = FS(c)->usesec;
+ FS(nc)->checksec = FS(c)->checksec;
+ }
+ return wq;
+}
+
+Chan*
+fsopen(Chan *c, int mode)
+{
+ HANDLE h;
+ int m, isdir, aflag, cflag;
+ char path[MAX_PATH];
+ wchar_t *wpath;
+
+ isdir = c->qid.type & QTDIR;
+ if(isdir && mode != OREAD)
+ error(Eperm);
+ fspath(FS(c)->name, 0, path, FS(c)->spec);
+
+ if(FS(c)->checksec) {
+ switch(mode & (OTRUNC|3)) {
+ case OREAD:
+ seccheck(path, RMODE, FS(c)->srv);
+ break;
+ case OWRITE:
+ case OWRITE|OTRUNC:
+ seccheck(path, WMODE, FS(c)->srv);
+ break;
+ case ORDWR:
+ case ORDWR|OTRUNC:
+ case OREAD|OTRUNC:
+ seccheck(path, RMODE|WMODE, FS(c)->srv);
+ break;
+ case OEXEC:
+ seccheck(path, XMODE, FS(c)->srv);
+ break;
+ default:
+ error(Ebadarg);
+ }
+ }
+
+ c->mode = openmode(mode);
+ if(isdir)
+ FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
+ else {
+ m = fsomode(mode & 3);
+ cflag = OPEN_EXISTING;
+ if(mode & OTRUNC)
+ cflag = TRUNCATE_EXISTING;
+ aflag = FILE_FLAG_RANDOM_ACCESS;
+ if(mode & ORCLOSE)
+ aflag |= FILE_FLAG_DELETE_ON_CLOSE;
+ if (winfileclash(path))
+ error(Eexist);
+ wpath = widen(path);
+ h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, 0, cflag, aflag, 0);
+ free(wpath);
+ if(h == INVALID_HANDLE_VALUE)
+ oserror();
+ FS(c)->fd = nth2fd(h);
+ }
+
+ c->offset = 0;
+ FS(c)->offset = 0;
+ c->flag |= COPEN;
+ return c;
+}
+
+void
+fscreate(Chan *c, char *name, int mode, ulong perm)
+{
+ Stat st;
+ HANDLE h;
+ int m, aflag;
+ SECURITY_ATTRIBUTES sa;
+ SECURITY_DESCRIPTOR *sd;
+ BY_HANDLE_FILE_INFORMATION hi;
+ char *p, path[MAX_PATH], sdrock[SD_ROCK];
+ wchar_t *wpath;
+ ACL *acl;
+
+ if(!okelem(name, 1))
+ error(Efilename);
+
+ m = fsomode(mode & 3);
+ p = fspath(FS(c)->name, name, path, FS(c)->spec);
+ acl = (ACL*)smalloc(ACL_ROCK);
+ sd = nil;
+ if(FS(c)->usesec) {
+ *p = '\0';
+ sd = secsd(path, sdrock);
+ *p = '\\';
+ if(sd == nil){
+ free(acl);
+ oserror();
+ }
+ if(FS(c)->checksec && !secsdhasperm(sd, WMODE, FS(c)->srv)
+ || !secsdstat(sd, &st, FS(c)->srv)){
+ if(sd != (void*)sdrock)
+ free(sd);
+ free(acl);
+ error(Eperm);
+ }
+ if(sd != (void*)sdrock)
+ free(sd);
+ if(perm & DMDIR)
+ st.mode = (perm & ~0777) | (st.mode & perm & 0777);
+ else
+ st.mode = (perm & ~0666) | (st.mode & perm & 0666);
+ st.owner = up->env->ui;
+ if(!isserver)
+ st.owner = fsuser;
+ sd = secmksd(sdrock, &st, acl, perm & DMDIR);
+ if(sd == nil){
+ free(acl);
+ oserror();
+ }
+ }
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = sd;
+ sa.bInheritHandle = 0;
+
+ if(perm & DMDIR) {
+ if(mode != OREAD) {
+ free(acl);
+ error(Eisdir);
+ }
+ wpath = widen(path);
+ if(!CreateDirectory(wpath, &sa) || !fsexist(path, &c->qid)) {
+ free(wpath);
+ free(acl);
+ oserror();
+ }
+ free(wpath);
+ FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
+ }
+ else {
+ aflag = 0;
+ if(mode & ORCLOSE)
+ aflag = FILE_FLAG_DELETE_ON_CLOSE;
+ if (winfileclash(path))
+ error(Eexist);
+ wpath = widen(path);
+ h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, CREATE_ALWAYS, aflag, 0);
+ free(wpath);
+ if(h == INVALID_HANDLE_VALUE) {
+ free(acl);
+ oserror();
+ }
+ FS(c)->fd = nth2fd(h);
+ c->qid.path = fsqidpath(path);
+ c->qid.type = 0;
+ c->qid.vers = 0;
+ if(GetFileInformationByHandle(h, &hi))
+ c->qid.vers = unixtime(hi.ftLastWriteTime);
+ }
+
+ c->mode = openmode(mode);
+ c->offset = 0;
+ FS(c)->offset = 0;
+ c->flag |= COPEN;
+ FS(c)->name = fswalkpath(FS(c)->name, name, 0);
+ free(acl);
+}
+
+void
+fsclose(Chan *c)
+{
+ HANDLE h;
+
+ if(c->flag & COPEN){
+ h = ntfd2h(FS(c)->fd);
+ if(h != INVALID_HANDLE_VALUE){
+ if(c->qid.type & QTDIR)
+ FindClose(h);
+ else
+ CloseHandle(h);
+ }
+ }
+ if(c->flag & CRCLOSE){
+ if(!waserror()){
+ fsremove(c);
+ poperror();
+ }
+ return;
+ }
+ fsfree(c);
+}
+
+/*
+ * 64-bit seeks, using SetFilePointer because SetFilePointerEx
+ * is not supported by NT
+ */
+static void
+fslseek(HANDLE h, vlong offset)
+{
+ LONG hi;
+
+ if(offset <= 0x7fffffff){
+ if(SetFilePointer(h, (LONG)offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ oserror();
+ }else{
+ hi = offset>>32;
+ if(SetFilePointer(h, (LONG)offset, &hi, FILE_BEGIN) == INVALID_SET_FILE_POINTER &&
+ GetLastError() != NO_ERROR)
+ oserror();
+ }
+}
+
+long
+fsread(Chan *c, void *va, long n, vlong offset)
+{
+ DWORD n2;
+ HANDLE h;
+
+ qlock(&FS(c)->oq);
+ if(waserror()){
+ qunlock(&FS(c)->oq);
+ nexterror();
+ }
+ if(c->qid.type & QTDIR) {
+ n2 = fsdirread(c, va, n, offset);
+ }
+ else {
+ h = ntfd2h(FS(c)->fd);
+ if(FS(c)->offset != offset){
+ fslseek(h, offset);
+ FS(c)->offset = offset;
+ }
+ if(!ReadFile(h, va, n, &n2, NULL))
+ oserror();
+ FS(c)->offset += n2;
+ }
+ qunlock(&FS(c)->oq);
+ poperror();
+ return n2;
+}
+
+long
+fswrite(Chan *c, void *va, long n, vlong offset)
+{
+ DWORD n2;
+ HANDLE h;
+
+ qlock(&FS(c)->oq);
+ if(waserror()){
+ qunlock(&FS(c)->oq);
+ nexterror();
+ }
+ h = ntfd2h(FS(c)->fd);
+ if(FS(c)->offset != offset){
+ fslseek(h, offset);
+ FS(c)->offset = offset;
+ }
+ if(!WriteFile(h, va, n, &n2, NULL))
+ oserror();
+ FS(c)->offset += n2;
+ qunlock(&FS(c)->oq);
+ poperror();
+ return n2;
+}
+
+int
+fsstat(Chan *c, uchar *buf, int n)
+{
+ WIN32_FIND_DATA data;
+ char path[MAX_PATH];
+ wchar_t *wpath;
+
+ /*
+ * have to fake up a data for volumes like
+ * c: and \\server\share since you can't FindFirstFile them
+ */
+ if(fsisroot(c)){
+ strcpy(path, rootdir);
+ if(strchr(path, '\\') == nil)
+ strcat(path, "\\.");
+ wpath = widen(path);
+ data.dwFileAttributes = GetFileAttributes(wpath);
+ free(wpath);
+ if(data.dwFileAttributes == 0xffffffff)
+ oserror();
+ data.ftCreationTime =
+ data.ftLastAccessTime =
+ data.ftLastWriteTime = wintime(time(0));
+ data.nFileSizeHigh = 0;
+ data.nFileSizeLow = 0;
+ utftorunes16(data.cFileName, ".", MAX_PATH);
+ } else {
+ HANDLE h = INVALID_HANDLE_VALUE;
+
+ fspath(FS(c)->name, 0, path, FS(c)->spec);
+ if (c->flag & COPEN)
+ h = ntfd2h(FS(c)->fd);
+
+ if (h != INVALID_HANDLE_VALUE) {
+ BY_HANDLE_FILE_INFORMATION fi;
+ if (c->mode & OWRITE)
+ FlushFileBuffers(h);
+ if (!GetFileInformationByHandle(h, &fi))
+ oserror();
+ data.dwFileAttributes = fi.dwFileAttributes;
+ data.ftCreationTime = fi.ftCreationTime;
+ data.ftLastAccessTime = fi.ftLastAccessTime;
+ data.ftLastWriteTime = fi.ftLastWriteTime;;
+ data.nFileSizeHigh = fi.nFileSizeHigh;
+ data.nFileSizeLow = fi.nFileSizeLow;
+ } else {
+ wpath = widen(path);
+ h = FindFirstFile(wpath, &data);
+ free(wpath);
+ if(h == INVALID_HANDLE_VALUE)
+ oserror();
+ if (!winfilematch(path, &data)) {
+ FindClose(h);
+ error(Enonexist);
+ }
+ FindClose(h);
+ }
+ utftorunes16(data.cFileName, fslastelem(FS(c)->name), MAX_PATH);
+ }
+
+ return fsdirset(buf, n, &data, path, c, 0);
+}
+
+int
+fswstat(Chan *c, uchar *buf, int n)
+{
+ int wsd;
+ Dir dir;
+ Stat st;
+ Cname * volatile ph;
+ HANDLE h;
+ ulong attr;
+ User *ou, *gu;
+ WIN32_FIND_DATA data;
+ SECURITY_DESCRIPTOR *sd;
+ char *last, sdrock[SD_ROCK], path[MAX_PATH], newpath[MAX_PATH], strs[4*256];
+ wchar_t wspath[MAX_PATH], wsnewpath[MAX_PATH];
+ wchar_t *wpath;
+ int nmatch;
+
+ n = convM2D(buf, n, &dir, strs);
+ if(n == 0)
+ error(Eshortstat);
+
+ last = fspath(FS(c)->name, 0, path, FS(c)->spec);
+ utftorunes16(wspath, path, MAX_PATH);
+
+ if(fsisroot(c)){
+ if(dir.atime != ~0)
+ data.ftLastAccessTime = wintime(dir.atime);
+ if(dir.mtime != ~0)
+ data.ftLastWriteTime = wintime(dir.mtime);
+ utftorunes16(data.cFileName, ".", MAX_PATH);
+ }else{
+ h = FindFirstFile(wspath, &data);
+ if(h == INVALID_HANDLE_VALUE)
+ oserror();
+ if (!winfilematch(path, &data)) {
+ FindClose(h);
+ error(Enonexist);
+ }
+ FindClose(h);
+ }
+
+ wsd = 0;
+ ou = nil;
+ gu = nil;
+ if(FS(c)->usesec) {
+ if(FS(c)->checksec && up->env->ui == fsnone)
+ error(Eperm);
+
+ /*
+ * find new owner and group
+ */
+ if(!emptystr(dir.uid)){
+ ou = unametouser(FS(c)->srv, dir.uid);
+ if(ou == nil)
+ oserror();
+ }
+ if(!emptystr(dir.gid)){
+ gu = unametouser(FS(c)->srv, dir.gid);
+ if(gu == nil){
+ if(strcmp(dir.gid, "unknown") != 0
+ && strcmp(dir.gid, "deleted") != 0)
+ oserror();
+ gu = ou;
+ }
+ }
+
+ /*
+ * find old stat info
+ */
+ sd = secsd(path, sdrock);
+ if(sd == nil || !secsdstat(sd, &st, FS(c)->srv)){
+ if(sd != nil && sd != (void*)sdrock)
+ free(sd);
+ oserror();
+ }
+ if(sd != (void*)sdrock)
+ free(sd);
+
+ /*
+ * permission rules:
+ * if none, can't do anything
+ * chown => no way
+ * chgrp => current owner or group, and in new group
+ * mode/time => owner or in either group
+ * rename => write in parent
+ */
+ if(ou == nil)
+ ou = st.owner;
+ if(FS(c)->checksec && st.owner != ou)
+ error(Eperm);
+
+ if(gu == nil)
+ gu = st.group;
+ if(st.group != gu){
+ if(FS(c)->checksec
+ &&(!ismember(up->env->ui, ou) && !ismember(up->env->ui, gu)
+ || !ismember(up->env->ui, st.group)))
+ error(Eperm);
+ wsd = 1;
+ }
+
+ if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime
+ || dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime
+ || dir.mode != ~0 && st.mode != dir.mode){
+ if(FS(c)->checksec
+ && !ismember(up->env->ui, ou)
+ && !ismember(up->env->ui, gu)
+ && !ismember(up->env->ui, st.group))
+ error(Eperm);
+ if(dir.mode != ~0 && st.mode != dir.mode)
+ wsd = 1;
+ }
+ }
+ wpath = widen(dir.name);
+ nmatch = runes16cmp(wpath, data.cFileName);
+ free(wpath);
+ if(!emptystr(dir.name) && nmatch != 0){
+ if(!okelem(dir.name, 1))
+ error(Efilename);
+ ph = fswalkpath(FS(c)->name, "..", 1);
+ if(waserror()){
+ cnameclose(ph);
+ nexterror();
+ }
+ ph = fswalkpath(ph, dir.name, 0);
+ fspath(ph, 0, newpath, FS(c)->spec);
+ utftorunes16(wsnewpath, newpath, MAX_PATH);
+ if(GetFileAttributes(wpath) != 0xffffffff && !winfileclash(newpath))
+ error("file already exists");
+ if(fsisroot(c))
+ error(Eperm);
+ if(FS(c)->checksec){
+ *last = '\0';
+ seccheck(path, WMODE, FS(c)->srv);
+ *last = '\\';
+ }
+ poperror();
+ cnameclose(ph);
+ }
+
+ if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime
+ || dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime)
+ fssettime(path, dir.atime, dir.mtime);
+
+ attr = data.dwFileAttributes;
+ if(dir.mode & 0222)
+ attr &= ~FILE_ATTRIBUTE_READONLY;
+ else
+ attr |= FILE_ATTRIBUTE_READONLY;
+ if(!fsisroot(c)
+ && attr != data.dwFileAttributes
+ && (attr & FILE_ATTRIBUTE_READONLY))
+ SetFileAttributes(wspath, attr);
+ if(FS(c)->usesec && wsd){
+ ACL *acl = (ACL *) smalloc(ACL_ROCK);
+ st.owner = ou;
+ st.group = gu;
+ if(dir.mode != ~0)
+ st.mode = dir.mode;
+ sd = secmksd(sdrock, &st, acl, data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+ if(sd == nil || !SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd)){
+ free(acl);
+ oserror();
+ }
+ free(acl);
+ }
+
+ if(!fsisroot(c)
+ && attr != data.dwFileAttributes
+ && !(attr & FILE_ATTRIBUTE_READONLY))
+ SetFileAttributes(wspath, attr);
+
+ /* do last so path is valid throughout */
+ wpath = widen(dir.name);
+ nmatch = runes16cmp(wpath, data.cFileName);
+ free(wpath);
+ if(!emptystr(dir.name) && nmatch != 0) {
+ ph = fswalkpath(FS(c)->name, "..", 1);
+ if(waserror()){
+ cnameclose(ph);
+ nexterror();
+ }
+ ph = fswalkpath(ph, dir.name, 0);
+ fspath(ph, 0, newpath, FS(c)->spec);
+ utftorunes16(wsnewpath, newpath, MAX_PATH);
+ /*
+ * can't rename if it is open: if this process has it open, close it temporarily.
+ */
+ if(!file_share_delete && c->flag & COPEN){
+ h = ntfd2h(FS(c)->fd);
+ if(h != INVALID_HANDLE_VALUE)
+ CloseHandle(h); /* woe betide it if ORCLOSE */
+ FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
+ }
+ if(!MoveFile(wspath, wsnewpath)) {
+ oserror();
+ } else if(!file_share_delete && c->flag & COPEN) {
+ int aflag;
+ SECURITY_ATTRIBUTES sa;
+
+ /* The move succeeded, so open new file to maintain handle */
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = sd;
+ sa.bInheritHandle = 0;
+ if(c->flag & CRCLOSE)
+ aflag = FILE_FLAG_DELETE_ON_CLOSE;
+ h = CreateFile(wsnewpath, fsomode(c->mode & 0x3), FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, OPEN_EXISTING, aflag, 0);
+ if(h == INVALID_HANDLE_VALUE)
+ oserror();
+ FS(c)->fd = nth2fd(h);
+ }
+ cnameclose(FS(c)->name);
+ poperror();
+ FS(c)->name = ph;
+ }
+ return n;
+}
+
+static void
+fsremove(Chan *c)
+{
+ int n;
+ char *p, path[MAX_PATH];
+ wchar_t wspath[MAX_PATH];
+
+ if(waserror()){
+ fsfree(c);
+ nexterror();
+ }
+ if(fsisroot(c))
+ error(Eperm);
+ p = fspath(FS(c)->name, 0, path, FS(c)->spec);
+ utftorunes16(wspath, path, MAX_PATH);
+ if(FS(c)->checksec){
+ *p = '\0';
+ seccheck(path, WMODE, FS(c)->srv);
+ *p = '\\';
+ }
+ if(c->qid.type & QTDIR)
+ n = RemoveDirectory(wspath);
+ else
+ n = DeleteFile(wspath);
+ if (!n) {
+ ulong attr, mode;
+ SECURITY_DESCRIPTOR *sd = nil;
+ char sdrock[SD_ROCK];
+ Stat st;
+ int secok;
+ attr = GetFileAttributes(wspath);
+ if(attr != 0xFFFFFFFF) {
+ if (FS(c)->usesec) {
+ sd = secsd(path, sdrock);
+ secok = (sd != nil) && secsdstat(sd, &st, FS(c)->srv);
+ if (secok) {
+ ACL *acl = (ACL *) smalloc(ACL_ROCK);
+ mode = st.mode;
+ st.mode |= 0660;
+ sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY);
+ if(sd != nil) {
+ SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd);
+ }
+ free(acl);
+ if(sd != nil && sd != (void*)sdrock)
+ free(sd);
+ sd = nil;
+ }
+ }
+ SetFileAttributes(wspath, FILE_ATTRIBUTE_NORMAL);
+ if(c->qid.type & QTDIR)
+ n = RemoveDirectory(wspath);
+ else
+ n = DeleteFile(wspath);
+ if (!n) {
+ if (FS(c)->usesec && secok) {
+ ACL *acl = (ACL *) smalloc(ACL_ROCK);
+ st.mode = mode;
+ sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY);
+ if(sd != nil) {
+ SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd);
+ }
+ free(acl);
+ }
+ SetFileAttributes(wspath, attr);
+ if(sd != nil && sd != (void*)sdrock)
+ free(sd);
+ }
+ }
+ }
+ if(!n)
+ oserror();
+ poperror();
+ fsfree(c);
+}
+
+/*
+ * check elem for illegal characters /\:*?"<>
+ * ... and relatives are also disallowed,
+ * since they specify grandparents, which we
+ * are not prepared to handle
+ */
+static int
+okelem(char *elem, int nodots)
+{
+ int c, dots;
+
+ dots = 0;
+ while((c = *(uchar*)elem) != 0){
+ if(isntfrog[c])
+ return 0;
+ if(c == '.' && dots >= 0)
+ dots++;
+ else
+ dots = -1;
+ elem++;
+ }
+ if(nodots)
+ return dots <= 0;
+ return dots <= 2;
+}
+
+static int
+cnisroot(Cname *c)
+{
+ return strcmp(c->s, "/") == 0;
+}
+
+static int
+fsisroot(Chan *c)
+{
+ return strcmp(FS(c)->name->s, "/") == 0;
+}
+
+static char*
+fspath(Cname *c, char *ext, char *path, char *spec)
+{
+ char *p, *last, *rootd;
+ int extlen = 0;
+
+ rootd = spec != nil ? spec : rootdir;
+ if(ext)
+ extlen = strlen(ext) + 1;
+ if(strlen(rootd) + extlen >= MAX_PATH)
+ error(Etoolong);
+ strcpy(path, rootd);
+ if(cnisroot(c)){
+ if(ext) {
+ strcat(path, "\\");
+ strcat(path, ext);
+ }
+ }else{
+ if(*c->s != '/') {
+ if(strlen(path) + 1 >= MAX_PATH)
+ error(Etoolong);
+ strcat(path, "\\");
+ }
+ if(strlen(path) + strlen(c->s) + extlen >= MAX_PATH)
+ error(Etoolong);
+ strcat(path, c->s);
+ if(ext){
+ strcat(path, "\\");
+ strcat(path, ext);
+ }
+ }
+ last = path;
+ for(p = path; *p != '\0'; p++){
+ if(*p == '/' || *p == '\\'){
+ *p = '\\';
+ last = p;
+ }
+ }
+ return last;
+}
+
+extern void cleancname(Cname*);
+
+static Cname *
+fswalkpath(Cname *c, char *name, int dup)
+{
+ if(dup)
+ c = newcname(c->s);
+ c = addelem(c, name);
+ if(isdotdot(name))
+ cleancname(c);
+ return c;
+}
+
+static char *
+fslastelem(Cname *c)
+{
+ char *p;
+
+ p = c->s + c->len;
+ while(p > c->s && p[-1] != '/')
+ p--;
+ return p;
+}
+
+static int
+fsdirbadentry(WIN32_FIND_DATA *data)
+{
+ wchar_t *s;
+
+ s = data->cFileName;
+ if(s[0] == 0)
+ return 1;
+ if(s[0] == '.' && (s[1] == 0 || s[1] == '.' && s[2] == 0))
+ return 1;
+
+ return 0;
+}
+
+static Fsdir*
+fsdirent(Chan *c, char *path, Fsdir *data)
+{
+ wchar_t *wpath;
+ HANDLE h;
+
+ h = ntfd2h(FS(c)->fd);
+ if(data == nil)
+ data = smalloc(sizeof(*data));
+ if(FS(c)->offset == 0){
+ if(h != INVALID_HANDLE_VALUE)
+ FindClose(h);
+ wpath = widen(path);
+ h = FindFirstFile(wpath, data);
+ free(wpath);
+ FS(c)->fd = nth2fd(h);
+ if(h == INVALID_HANDLE_VALUE){
+ free(data);
+ return nil;
+ }
+ if(!fsdirbadentry(data))
+ return data;
+ }
+ do{
+ if(!FindNextFile(h, data)){
+ free(data);
+ return nil;
+ }
+ }while(fsdirbadentry(data));
+ return data;
+}
+
+static long
+fsdirread(Chan *c, uchar *va, int count, vlong offset)
+{
+ int i, r;
+ char path[MAX_PATH], *p;
+ Fsdir *de;
+ vlong o;
+
+ if(count == 0 || offset < 0)
+ return 0;
+ p = fspath(FS(c)->name, "*.*", path, FS(c)->spec);
+ p++;
+ de = nil;
+ if(FS(c)->offset != offset){
+ de = FS(c)->de;
+ if(FS(c)->de != nil){
+ free(FS(c)->de);
+ FS(c)->de = nil;
+ }
+ FS(c)->offset = 0;
+ for(o = 0; o < offset;){
+ de = fsdirent(c, path, de);
+ if(de == nil){
+ FS(c)->offset = o;
+ return 0;
+ }
+ runes16toutf(p, de->cFileName, &path[MAX_PATH]-p);
+ path[MAX_PATH-1] = '\0';
+ o += fsdirsize(de, path, c);
+ }
+ FS(c)->offset = offset;
+ }
+ for(i = 0; i < count;){
+ if(FS(c)->de != nil){ /* left over from previous read at offset */
+ de = FS(c)->de;
+ FS(c)->de = nil;
+ }else{
+ de = fsdirent(c, path, de);
+ if(de == nil)
+ break;
+ }
+ runes16toutf(p, de->cFileName, &path[MAX_PATH]-p);
+ path[MAX_PATH-1] = '\0';
+ r = fsdirset(va+i, count-i, de, path, c, 1);
+ if(r <= 0){
+ /* won't fit; save for next read at this offset */
+ FS(c)->de = de;
+ break;
+ }
+ i += r;
+ FS(c)->offset += r;
+ }
+ return i;
+}
+
+static ulong
+fsqidpath(char *p)
+{
+ ulong h;
+ int c;
+
+ h = 0;
+ while(*p != '\0'){
+ /* force case insensitive file names */
+ c = *p++;
+ if(c >= 'A' && c <= 'Z')
+ c += 'a'-'A';
+ h = h * 19 ^ c;
+ }
+ return h;
+}
+
+/* TO DO: substitute fixed, made-up (unlikely) names for these */
+static char* devf[] = { "aux", "com1", "com2", "lpt1", "nul", "prn", nil };
+
+static int
+devfile(char *p)
+{
+ char *s, *t, *u, **ss;
+
+ if((u = strrchr(p, '\\')) != nil)
+ u++;
+ else if((u = strrchr(p, '/')) != nil)
+ u++;
+ else
+ u = p;
+ for(ss = devf; *ss != nil; ss++){
+ for(s = *ss, t = u; *s != '\0' && *t != '\0' && *t != '.'; s++, t++)
+ if(*s != *t && *s != *t+'a'-'A')
+ break;
+ if(*s == '\0' && (*t == '\0' || *t == '.'))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * there are other ways to figure out
+ * the attributes and times for a file.
+ * perhaps they are faster
+ */
+static int
+fsexist(char *p, Qid *q)
+{
+ HANDLE h;
+ WIN32_FIND_DATA data;
+ wchar_t *wpath;
+
+ if(devfile(p))
+ return 0;
+ wpath = widen(p);
+ h = FindFirstFile(wpath, &data);
+ free(wpath);
+ if(h == INVALID_HANDLE_VALUE)
+ return 0;
+ if (!winfilematch(p, &data)) {
+ FindClose(h);
+ return 0;
+ }
+ FindClose(h);
+
+ q->path = fsqidpath(p);
+ q->type = 0;
+
+ if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ q->type |= QTDIR;
+
+ q->vers = unixtime(data.ftLastWriteTime);
+
+ return 1;
+}
+
+static int
+fsdirset(char *edir, int n, WIN32_FIND_DATA *data, char *path, Chan *c, int isdir)
+{
+ Dir dir;
+ static char neveryone[] = "Everyone";
+
+ dir.name = narrowen(data->cFileName);
+ dir.muid = nil;
+ dir.qid.path = fsqidpath(path);
+ dir.qid.vers = 0;
+ dir.qid.type = 0;
+ dir.mode = 0;
+ dir.atime = unixtime(data->ftLastAccessTime);
+ dir.mtime = unixtime(data->ftLastWriteTime);
+ dir.qid.vers = dir.mtime;
+ dir.length = ((uvlong)data->nFileSizeHigh<<32) | ((uvlong)data->nFileSizeLow & ~((uvlong)0xFFFFFFFF<<32));
+ dir.type = 'U';
+ dir.dev = c->dev;
+
+ if(!FS(c)->usesec){
+ /* no NT security so make something up */
+ dir.uid = neveryone;
+ dir.gid = neveryone;
+ dir.mode = 0777;
+ }else if(!secstat(&dir, path, FS(c)->srv))
+ oserror();
+
+ if(data->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+ dir.mode &= ~0222;
+ if(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
+ dir.qid.type |= QTDIR;
+ dir.mode |= DMDIR;
+ dir.length = 0;
+ }
+
+ if(isdir && sizeD2M(&dir) > n)
+ n = -1;
+ else
+ n = convD2M(&dir, edir, n);
+ if(dir.uid != neveryone)
+ free(dir.uid);
+ if(dir.gid != neveryone)
+ free(dir.gid);
+ free(dir.name);
+ return n;
+}
+
+static int
+fsdirsize(WIN32_FIND_DATA *data, char *path, Chan *c)
+{
+ int i, n;
+
+ n = widebytes(data->cFileName);
+ if(!FS(c)->usesec)
+ n += 8+8;
+ else{
+ i = secsize(path, FS(c)->srv);
+ if(i < 0)
+ oserror();
+ n += i;
+ }
+ return STATFIXLEN+n;
+}
+
+static void
+fssettime(char *path, long at, long mt)
+{
+ HANDLE h;
+ FILETIME atime, mtime;
+ wchar_t *wpath;
+
+ wpath = widen(path);
+ h = CreateFile(wpath, GENERIC_WRITE,
+ 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ free(wpath);
+ if(h == INVALID_HANDLE_VALUE)
+ return;
+ mtime = wintime(mt);
+ atime = wintime(at);
+ if(!SetFileTime(h, 0, &atime, &mtime)){
+ CloseHandle(h);
+ oserror();
+ }
+ CloseHandle(h);
+}
+
+static int
+fsomode(int m)
+{
+ switch(m & 0x3) {
+ case OREAD:
+ case OEXEC:
+ return GENERIC_READ;
+ case OWRITE:
+ return GENERIC_WRITE;
+ case ORDWR:
+ return GENERIC_READ|GENERIC_WRITE;
+ }
+ error(Ebadarg);
+ return 0;
+}
+
+static long
+unixtime(FILETIME ft)
+{
+ vlong t;
+
+ t = (vlong)ft.dwLowDateTime + ((vlong)ft.dwHighDateTime<<32);
+ t -= (vlong)10000000*134774*24*60*60;
+
+ return (long)(t/10000000);
+}
+
+static FILETIME
+wintime(ulong t)
+{
+ FILETIME ft;
+ vlong vt;
+
+ vt = (vlong)t*10000000+(vlong)10000000*134774*24*60*60;
+
+ ft.dwLowDateTime = vt;
+ ft.dwHighDateTime = vt>>32;
+
+ return ft;
+}
+
+/*
+ * the sec routines manage file permissions for nt.
+ * nt files have an associated security descriptor,
+ * which has in it an owner, a group,
+ * and a discretionary acces control list, or acl,
+ * which specifies the permissions for the file.
+ *
+ * the strategy for mapping between inferno owner,
+ * group, other, and mode and nt file security is:
+ *
+ * inferno owner == nt file owner
+ * inferno other == nt Everyone
+ * inferno group == first non-owner,
+ * non-Everyone user given in the acl,
+ * or the owner if there is no such user.
+ * we examine the entire acl when check for permissions,
+ * but only report a subset.
+ *
+ * when we write an acl, we also give all permissions to
+ * the special user rootname, who is supposed to run emu in server mode.
+ */
+static void
+secinit(void)
+{
+ HANDLE token;
+ TOKEN_PRIVILEGES *priv;
+ char privrock[sizeof(TOKEN_PRIVILEGES) + 1*sizeof(LUID_AND_ATTRIBUTES)];
+ SID_IDENTIFIER_AUTHORITY id = SECURITY_CREATOR_SID_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY wid = SECURITY_WORLD_SID_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY ntid = SECURITY_NT_AUTHORITY;
+
+ if(!AllocateAndInitializeSid(&id, 1,
+ SECURITY_CREATOR_OWNER_RID,
+ 1, 2, 3, 4, 5, 6, 7, &creatorowner)
+ || !AllocateAndInitializeSid(&id, 1,
+ SECURITY_CREATOR_GROUP_RID,
+ 1, 2, 3, 4, 5, 6, 7, &creatorgroup)
+ || !AllocateAndInitializeSid(&wid, 1,
+ SECURITY_WORLD_RID,
+ 1, 2, 3, 4, 5, 6, 7, &everyone)
+ || !AllocateAndInitializeSid(&ntid, 1,
+ 0,
+ 1, 2, 3, 4, 5, 6, 7, &ntignore))
+ panic("can't initialize well-known sids");
+
+ fsnone = sidtouser(ntsrv, everyone);
+ if(fsnone == nil)
+ panic("can't make none user");
+
+ /*
+ * see if we are running as the emu server user
+ * if so, set up SE_RESTORE_NAME privilege,
+ * which allows setting the owner field in a security descriptor.
+ * other interesting privileges are SE_TAKE_OWNERSHIP_NAME,
+ * which enables changing the ownership of a file to yourself
+ * regardless of the permissions on the file, SE_BACKUP_NAME,
+ * which enables reading any files regardless of permission,
+ * and SE_CHANGE_NOTIFY_NAME, which enables walking through
+ * directories without X permission.
+ * SE_RESTORE_NAME and SE_BACKUP_NAME together allow writing
+ * and reading any file data, regardless of permission,
+ * if the file is opened with FILE_BACKUP_SEMANTICS.
+ */
+ isserver = 0;
+ fsuser = secuser();
+ if(fsuser == nil)
+ fsuser = fsnone;
+ else if(runes16cmp(fsuser->name, rootname) == 0
+ && OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)){
+ priv = (TOKEN_PRIVILEGES*)privrock;
+ priv->PrivilegeCount = 1;
+ priv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ if(LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &priv->Privileges[0].Luid)
+ && AdjustTokenPrivileges(token, 0, priv, 0, NULL, NULL))
+ isserver = 1;
+ CloseHandle(token);
+ }
+}
+
+/*
+ * get the User for the executing process
+ */
+static User*
+secuser(void)
+{
+ DWORD need;
+ HANDLE token;
+ TOKEN_USER *tu;
+ char turock[sizeof(TOKEN_USER) + MAX_SID];
+
+ if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
+ return nil;
+
+ tu = (TOKEN_USER*)turock;
+ if(!GetTokenInformation(token, TokenUser, tu, sizeof(turock), &need)){
+ CloseHandle(token);
+ return nil;
+ }
+ CloseHandle(token);
+ return sidtouser(nil, tu->User.Sid);
+}
+
+static int
+secstat(Dir *dir, char *file, Rune16 *srv)
+{
+ int ok, n;
+ Stat st;
+ char sdrock[SD_ROCK];
+ SECURITY_DESCRIPTOR *sd;
+
+ sd = secsd(file, sdrock);
+ if(sd == nil){
+ int e = GetLastError();
+ if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION){
+ dir->uid = strdup("unknown");
+ dir->gid = strdup("unknown");
+ if(dir->uid == nil || dir->gid == nil){
+ free(dir->uid);
+ error(Enomem); /* will change to use kstrdup */
+ }
+ dir->mode = 0;
+ return 1;
+ }
+ return 0;
+ }
+ ok = secsdstat(sd, &st, srv);
+ if(sd != (void*)sdrock)
+ free(sd);
+ if(ok){
+ dir->mode = st.mode;
+ n = rune16nlen(st.owner->name, runes16len(st.owner->name));
+ dir->uid = smalloc(n+1);
+ runes16toutf(dir->uid, st.owner->name, n+1);
+ n = rune16nlen(st.group->name, runes16len(st.group->name));
+ dir->gid = smalloc(n+1);
+ runes16toutf(dir->gid, st.group->name, n+1);
+ }
+ return ok;
+}
+
+static int
+secsize(char *file, Rune16 *srv)
+{
+ int ok;
+ Stat st;
+ char sdrock[SD_ROCK];
+ SECURITY_DESCRIPTOR *sd;
+
+ sd = secsd(file, sdrock);
+ if(sd == nil){
+ int e = GetLastError();
+ if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION)
+ return 7+7;
+ return -1;
+ }
+ ok = secsdstat(sd, &st, srv);
+ if(sd != (void*)sdrock)
+ free(sd);
+ if(ok)
+ return rune16nlen(st.owner->name, runes16len(st.owner->name))+rune16nlen(st.group->name, runes16len(st.group->name));
+ return -1;
+}
+
+/*
+ * verify that u had access to file
+ */
+static void
+seccheck(char *file, ulong access, Rune16 *srv)
+{
+ if(!sechasperm(file, access, srv))
+ error(Eperm);
+}
+
+static int
+sechasperm(char *file, ulong access, Rune16 *srv)
+{
+ int ok;
+ char sdrock[SD_ROCK];
+ SECURITY_DESCRIPTOR *sd;
+
+ /*
+ * only really needs dacl info
+ */
+ sd = secsd(file, sdrock);
+ if(sd == nil)
+ return 0;
+ ok = secsdhasperm(sd, access, srv);
+ if(sd != (void*)sdrock)
+ free(sd);
+ return ok;
+}
+
+static SECURITY_DESCRIPTOR*
+secsd(char *file, char sdrock[SD_ROCK])
+{
+ DWORD need;
+ SECURITY_DESCRIPTOR *sd;
+ char *path, pathrock[6];
+ wchar_t *wpath;
+
+ path = file;
+ if(path[0] != '\0' && path[1] == ':' && path[2] == '\0'){
+ path = pathrock;
+ strcpy(path, "?:\\.");
+ path[0] = file[0];
+ }
+ sd = (SECURITY_DESCRIPTOR*)sdrock;
+ need = 0;
+ wpath = widen(path);
+ if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, SD_ROCK, &need)) {
+ free(wpath);
+ return sd;
+ }
+ if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ free(wpath);
+ return nil;
+ }
+ sd = malloc(need);
+ if(sd == nil) {
+ free(wpath);
+ error(Enomem);
+ }
+ if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, need, &need)) {
+ free(wpath);
+ return sd;
+ }
+ free(wpath);
+ free(sd);
+ return nil;
+}
+
+static int
+secsdstat(SECURITY_DESCRIPTOR *sd, Stat *st, Rune16 *srv)
+{
+ ACL *acl;
+ BOOL hasacl, b;
+ ACE_HEADER *aceh;
+ User *owner, *group;
+ SID *sid, *osid, *gsid;
+ ACCESS_ALLOWED_ACE *ace;
+ int i, allow, deny, *p, m;
+ ACL_SIZE_INFORMATION size;
+
+ st->mode = 0;
+
+ osid = nil;
+ gsid = nil;
+ if(!GetSecurityDescriptorOwner(sd, &osid, &b)
+ || !GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b))
+ return 0;
+
+ if(acl == 0)
+ size.AceCount = 0;
+ else if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation))
+ return 0;
+
+ /*
+ * first pass through acl finds group
+ */
+ for(i = 0; i < size.AceCount; i++){
+ if(!GetAce(acl, i, &aceh))
+ continue;
+ if(aceh->AceFlags & INHERIT_ONLY_ACE)
+ continue;
+
+ if(aceh->AceType != ACCESS_ALLOWED_ACE_TYPE
+ && aceh->AceType != ACCESS_DENIED_ACE_TYPE)
+ continue;
+
+ ace = (ACCESS_ALLOWED_ACE*)aceh;
+ sid = (SID*)&ace->SidStart;
+ if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
+ continue;
+
+ if(EqualSid(sid, everyone))
+ ;
+ else if(EqualSid(sid, osid))
+ ;
+ else if(EqualPrefixSid(sid, ntignore))
+ continue; /* boring nt accounts */
+ else{
+ gsid = sid;
+ break;
+ }
+ }
+ if(gsid == nil)
+ gsid = osid;
+
+ owner = sidtouser(srv, osid);
+ if(owner == nil)
+ return 0;
+ group = sidtouser(srv, gsid);
+ if(group == nil)
+ return 0;
+
+ /* no acl means full access */
+ allow = 0;
+ if(acl == 0)
+ allow = 0777;
+ deny = 0;
+ for(i = 0; i < size.AceCount; i++){
+ if(!GetAce(acl, i, &aceh))
+ continue;
+ if(aceh->AceFlags & INHERIT_ONLY_ACE)
+ continue;
+
+ if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE)
+ p = &allow;
+ else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE)
+ p = &deny;
+ else
+ continue;
+
+ ace = (ACCESS_ALLOWED_ACE*)aceh;
+ sid = (SID*)&ace->SidStart;
+ if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
+ continue;
+
+ m = 0;
+ if(ace->Mask & FILE_EXECUTE)
+ m |= 1;
+ if(ace->Mask & FILE_WRITE_DATA)
+ m |= 2;
+ if(ace->Mask & FILE_READ_DATA)
+ m |= 4;
+
+ if(ismembersid(srv, owner, sid))
+ *p |= (m << 6) & ~(allow|deny) & 0700;
+ if(ismembersid(srv, group, sid))
+ *p |= (m << 3) & ~(allow|deny) & 0070;
+ if(EqualSid(everyone, sid))
+ *p |= m & ~(allow|deny) & 0007;
+ }
+
+ st->mode = allow & ~deny;
+ st->owner = owner;
+ st->group = group;
+ return 1;
+}
+
+static int
+secsdhasperm(SECURITY_DESCRIPTOR *sd, ulong access, Rune16 *srv)
+{
+ User *u;
+ ACL *acl;
+ BOOL hasacl, b;
+ ACE_HEADER *aceh;
+ SID *sid, *osid, *gsid;
+ int i, allow, deny, *p, m;
+ ACCESS_ALLOWED_ACE *ace;
+ ACL_SIZE_INFORMATION size;
+
+ u = up->env->ui;
+ allow = 0;
+ deny = 0;
+ osid = nil;
+ gsid = nil;
+ if(!GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b))
+ return 0;
+
+ /* no acl means full access */
+ if(acl == 0)
+ return 1;
+ if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation))
+ return 0;
+ for(i = 0; i < size.AceCount; i++){
+ if(!GetAce(acl, i, &aceh))
+ continue;
+ if(aceh->AceFlags & INHERIT_ONLY_ACE)
+ continue;
+
+ if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE)
+ p = &allow;
+ else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE)
+ p = &deny;
+ else
+ continue;
+
+ ace = (ACCESS_ALLOWED_ACE*)aceh;
+ sid = (SID*)&ace->SidStart;
+ if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
+ continue;
+
+ m = ace->Mask;
+
+ if(ismembersid(srv, u, sid))
+ *p |= m & ~(allow|deny);
+ }
+
+ allow &= ~deny;
+
+ return (allow & access) == access;
+}
+
+static SECURITY_DESCRIPTOR*
+secmksd(char *sdrock, Stat *st, ACL *dacl, int isdir)
+{
+ int m;
+
+ ulong mode;
+ ACE_HEADER *aceh;
+ SECURITY_DESCRIPTOR *sd;
+
+ sd = (SECURITY_DESCRIPTOR*)sdrock;
+ if(!InitializeAcl(dacl, ACL_ROCK, ACL_REVISION))
+ return nil;
+
+ mode = st->mode;
+ if(st->owner == st->group){
+ mode |= (mode >> 3) & 0070;
+ mode |= (mode << 3) & 0700;
+ }
+
+
+ m = modetomask[(mode>>6) & 7];
+ if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->owner->sid))
+ return nil;
+
+ if(isdir && !AddAccessAllowedAce(dacl, ACL_REVISION, m, creatorowner))
+ return nil;
+
+ m = modetomask[(mode>>3) & 7];
+ if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->group->sid))
+ return nil;
+
+ m = modetomask[(mode>>0) & 7];
+ if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, everyone))
+ return nil;
+
+ if(isdir){
+ /* hack to add inherit flags */
+ if(!GetAce(dacl, 1, &aceh))
+ return nil;
+ aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
+ if(!GetAce(dacl, 2, &aceh))
+ return nil;
+ aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
+ if(!GetAce(dacl, 3, &aceh))
+ return nil;
+ aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
+ }
+
+ /*
+ * allow server user to access any file
+ */
+ if(isserver){
+ if(!AddAccessAllowedAce(dacl, ACL_REVISION, RMODE|WMODE|XMODE, fsuser->sid))
+ return nil;
+ if(isdir){
+ if(!GetAce(dacl, 4, &aceh))
+ return nil;
+ aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
+ }
+ }
+
+ if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION))
+ return nil;
+ if(!SetSecurityDescriptorDacl(sd, 1, dacl, 0))
+ return nil;
+// if(isserver && !SetSecurityDescriptorOwner(sd, st->owner->sid, 0))
+// return nil;
+ return sd;
+}
+
+/*
+ * the user manipulation routines
+ * just make it easier to deal with user identities
+ */
+static User*
+sidtouser(Rune16 *srv, SID *s)
+{
+ SID_NAME_USE type;
+ Rune16 aname[100], dname[100];
+ DWORD naname, ndname;
+ User *u;
+
+ qlock(&users.lk);
+ for(u = users.u; u != 0; u = u->next)
+ if(EqualSid(s, u->sid))
+ break;
+ qunlock(&users.lk);
+
+ if(u != 0)
+ return u;
+
+ naname = sizeof(aname);
+ ndname = sizeof(dname);
+
+ if(!LookupAccountSidW(srv, s, aname, &naname, dname, &ndname, &type))
+ return mkuser(s, SidTypeUnknown, L"unknown", L"unknown");
+ return mkuser(s, type, aname, dname);
+}
+
+static User*
+domnametouser(Rune16 *srv, Rune16 *name, Rune16 *dom)
+{
+ User *u;
+
+ qlock(&users.lk);
+ for(u = users.u; u != 0; u = u->next)
+ if(runes16cmp(name, u->name) == 0 && runes16cmp(dom, u->dom) == 0)
+ break;
+ qunlock(&users.lk);
+ if(u == 0)
+ u = nametouser(srv, name);
+ return u;
+}
+
+static User*
+nametouser(Rune16 *srv, Rune16 *name)
+{
+ char sidrock[MAX_SID];
+ SID *sid;
+ SID_NAME_USE type;
+ Rune16 dom[MAX_PATH];
+ DWORD nsid, ndom;
+
+ sid = (SID*)sidrock;
+ nsid = sizeof(sidrock);
+ ndom = sizeof(dom);
+ if(!LookupAccountNameW(srv, name, sid, &nsid, dom, &ndom, &type))
+ return nil;
+
+ return mkuser(sid, type, name, dom);
+}
+
+/*
+ * this mapping could be cached
+ */
+static User*
+unametouser(Rune16 *srv, char *name)
+{
+ Rune16 rname[MAX_PATH];
+
+ utftorunes16(rname, name, MAX_PATH);
+ return nametouser(srv, rname);
+}
+
+/*
+ * make a user structure and add it to the global cache.
+ */
+static User*
+mkuser(SID *sid, int type, Rune16 *name, Rune16 *dom)
+{
+ User *u;
+
+ qlock(&users.lk);
+ for(u = users.u; u != 0; u = u->next){
+ if(EqualSid(sid, u->sid)){
+ qunlock(&users.lk);
+ return u;
+ }
+ }
+
+ switch(type) {
+ default:
+ break;
+ case SidTypeDeletedAccount:
+ name = L"deleted";
+ break;
+ case SidTypeInvalid:
+ name = L"invalid";
+ break;
+ case SidTypeUnknown:
+ name = L"unknown";
+ break;
+ }
+
+ u = malloc(sizeof(User));
+ if(u == nil){
+ qunlock(&users.lk);
+ return 0;
+ }
+ u->next = nil;
+ u->group = nil;
+ u->sid = dupsid(sid);
+ u->type = type;
+ u->name = nil;
+ if(name != nil)
+ u->name = runes16dup(name);
+ u->dom = nil;
+ if(dom != nil)
+ u->dom = runes16dup(dom);
+
+ u->next = users.u;
+ users.u = u;
+
+ qunlock(&users.lk);
+ return u;
+}
+
+/*
+ * check if u is a member of gsid,
+ * which might be a group.
+ */
+static int
+ismembersid(Rune16 *srv, User *u, SID *gsid)
+{
+ User *g;
+
+ if(EqualSid(u->sid, gsid))
+ return 1;
+
+ g = sidtouser(srv, gsid);
+ if(g == 0)
+ return 0;
+ return ismember(u, g);
+}
+
+static int
+ismember(User *u, User *g)
+{
+ Gmem *grps;
+
+ if(EqualSid(u->sid, g->sid))
+ return 1;
+
+ if(EqualSid(g->sid, everyone))
+ return 1;
+
+ qlock(&u->lk);
+ addgroups(u, 0);
+ for(grps = u->group; grps != 0; grps = grps->next){
+ if(EqualSid(grps->user->sid, g->sid)){
+ qunlock(&u->lk);
+ return 1;
+ }
+ }
+ qunlock(&u->lk);
+ return 0;
+}
+
+/*
+ * find out what groups a user belongs to.
+ * if force, throw out the old info and do it again.
+ *
+ * note that a global group is also know as a group,
+ * and a local group is also know as an alias.
+ * global groups can only contain users.
+ * local groups can contain global groups or users.
+ * this code finds all global groups to which a user belongs,
+ * and all the local groups to which the user or a global group
+ * containing the user belongs.
+ */
+static void
+addgroups(User *u, int force)
+{
+ LOCALGROUP_USERS_INFO_0 *loc;
+ GROUP_USERS_INFO_0 *grp;
+ DWORD i, n, rem;
+ User *gu;
+ Gmem *g, *next;
+ Rune16 *srv, srvrock[MAX_PATH];
+
+ if(force){
+ u->gotgroup = 0;
+ for(g = u->group; g != nil; g = next){
+ next = g->next;
+ free(g);
+ }
+ u->group = nil;
+ }
+ if(u->gotgroup)
+ return;
+ u->gotgroup = 1;
+
+ n = 0;
+ srv = domsrv(u->dom, srvrock);
+ i = NetUserGetGroups(srv, u->name, 0,
+ (BYTE**)&grp, MAX_PREFERRED_LENGTH, &n, &rem);
+ if(i == NERR_Success || i == ERROR_MORE_DATA){
+ for(i = 0; i < n; i++){
+ gu = domnametouser(srv, grp[i].grui0_name, u->dom);
+ if(gu == 0)
+ continue;
+ g = malloc(sizeof(Gmem));
+ if(g == nil)
+ error(Enomem);
+ g->user = gu;
+ g->next = u->group;
+ u->group = g;
+ }
+ NetApiBufferFree(grp);
+ }
+
+ n = 0;
+ i = NetUserGetLocalGroups(srv, u->name, 0, LG_INCLUDE_INDIRECT,
+ (BYTE**)&loc, MAX_PREFERRED_LENGTH, &n, &rem);
+ if(i == NERR_Success || i == ERROR_MORE_DATA){
+ for(i = 0; i < n; i++){
+ gu = domnametouser(srv, loc[i].lgrui0_name, u->dom);
+ if(gu == NULL)
+ continue;
+ g = malloc(sizeof(Gmem));
+ if(g == nil)
+ error(Enomem);
+ g->user = gu;
+ g->next = u->group;
+ u->group = g;
+ }
+ NetApiBufferFree(loc);
+ }
+}
+
+static SID*
+dupsid(SID *sid)
+{
+ SID *nsid;
+ int n;
+
+ n = GetLengthSid(sid);
+ nsid = malloc(n);
+ if(nsid == nil || !CopySid(n, nsid, sid))
+ panic("can't copy sid");
+ return nsid;
+}
+
+/*
+ * return the name of the server machine for file
+ */
+static Rune16*
+filesrv(char *file)
+{
+ int n;
+ Rune16 *srv;
+ char *p, uni[MAX_PATH], mfile[MAX_PATH];
+ wchar_t vol[3];
+
+ strcpy(mfile, file);
+ /* assume file is a fully qualified name - X: or \\server */
+ if(file[1] == ':') {
+ vol[0] = file[0];
+ vol[1] = file[1];
+ vol[2] = 0;
+ if(GetDriveType(vol) != DRIVE_REMOTE)
+ return 0;
+ n = sizeof(uni);
+ if(WNetGetUniversalName(vol, UNIVERSAL_NAME_INFO_LEVEL, uni, &n) != NO_ERROR)
+ return nil;
+ runes16toutf(mfile, ((UNIVERSAL_NAME_INFO*)uni)->lpUniversalName, MAX_PATH);
+ file = mfile;
+ }
+ file += 2;
+ p = strchr(file, '\\');
+ if(p == 0)
+ n = strlen(file);
+ else
+ n = p - file;
+ if(n >= MAX_PATH)
+ n = MAX_PATH-1;
+
+ memmove(uni, file, n);
+ uni[n] = '\0';
+
+ srv = malloc((n + 1) * sizeof(Rune16));
+ if(srv == nil)
+ panic("filesrv: no memory");
+ utftorunes16(srv, uni, n+1);
+ return srv;
+}
+
+/*
+ * does the file system support acls?
+ */
+static int
+fsacls(char *file)
+{
+ char *p;
+ DWORD flags;
+ char path[MAX_PATH];
+ wchar_t wpath[MAX_PATH];
+
+ /* assume file is a fully qualified name - X: or \\server */
+ if(file[1] == ':') {
+ path[0] = file[0];
+ path[1] = file[1];
+ path[2] = '\\';
+ path[3] = 0;
+ } else {
+ strcpy(path, file);
+ p = strchr(path+2, '\\');
+ if(p == 0)
+ return 0;
+ p = strchr(p+1, '\\');
+ if(p == 0)
+ strcat(path, "\\");
+ else
+ p[1] = 0;
+ }
+ utftorunes16(wpath, path, MAX_PATH);
+ if(!GetVolumeInformation(wpath, NULL, 0, NULL, NULL, &flags, NULL, 0))
+ return 0;
+
+ return flags & FS_PERSISTENT_ACLS;
+}
+
+/*
+ * given a domain, find out the server to ask about its users.
+ * we just ask the local machine to do the translation,
+ * so it might fail sometimes. in those cases, we don't
+ * trust the domain anyway, and vice versa, so it's not
+ * clear what benifit we would gain by getting the answer "right".
+ */
+static Rune16*
+domsrv(Rune16 *dom, Rune16 srv[MAX_PATH])
+{
+ Rune16 *psrv;
+ int n, r;
+
+ if(dom[0] == 0)
+ return nil;
+
+ r = NetGetAnyDCName(NULL, dom, (LPBYTE*)&psrv);
+ if(r == NERR_Success) {
+ n = runes16len(psrv);
+ if(n >= MAX_PATH)
+ n = MAX_PATH-1;
+ memmove(srv, psrv, n*sizeof(Rune16));
+ srv[n] = 0;
+ NetApiBufferFree(psrv);
+ return srv;
+ }
+
+ return nil;
+}
+
+Dev fsdevtab = {
+ 'U',
+ "fs",
+
+ fsinit,
+ fsattach,
+ fswalk,
+ fsstat,
+ fsopen,
+ fscreate,
+ fsclose,
+ fsread,
+ devbread,
+ fswrite,
+ devbwrite,
+ fsremove,
+ fswstat
+};
--- /dev/null
+++ b/emu/Nt/emu
@@ -1,0 +1,112 @@
+env
+ LDFLAGS= -subsystem:console -entry:WinMainCRTStartup $LDFLAGS
+
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win
+
+ ip ipif ipaux
+ eia
+ audio audio
+ mem
+ arch
+ pointer
+ snarf
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+ 9
+
+link
+
+mod
+ sys
+ draw
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Nt/fp.c
@@ -1,0 +1,102 @@
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef signed char schar;
+
+#include <float.h>
+#include "mathi.h"
+
+#define NANEXP (2047<<20)
+#define NANMASK (2047<<20)
+#define NANSIGN (1<<31)
+
+int isInf(double, int);
+
+double
+NaN(void)
+{
+ union
+ {
+ double d;
+ long x[2];
+ } a;
+
+ a.x[1] = NANEXP;
+ a.x[0] = 1;
+ return a.d;
+}
+
+int
+isNaN(double d)
+{
+ union
+ {
+ double d;
+ long x[2];
+ } a;
+
+ a.d = d;
+ if((a.x[1] & NANMASK) != NANEXP)
+ return 0;
+ return !isInf(d, 0);
+}
+
+double
+Inf(int sign)
+{
+ union
+ {
+ double d;
+ long x[2];
+ } a;
+
+ a.x[1] = NANEXP;
+ a.x[0] = 0;
+ if(sign < 0)
+ a.x[1] |= NANSIGN;
+ return a.d;
+}
+
+int
+isInf(double d, int sign)
+{
+ union
+ {
+ double d;
+ long x[2];
+ } a;
+
+ a.d = d;
+ if(a.x[0] != 0)
+ return 0;
+ if(a.x[1] == NANEXP)
+ return sign >= 0;
+ if(a.x[1] == (NANEXP|NANSIGN))
+ return sign <= 0;
+ return 0;
+}
+
+ulong
+getfcr(void)
+{
+ return getFPcontrol();
+}
+
+void
+setfcr(ulong m)
+{
+ FPcontrol(m, ~0);
+}
+
+ulong
+getfsr(void)
+{
+ return getFPstatus();
+}
+
+void
+setfsr(ulong m)
+{
+ FPstatus(m, ~0);
+}
--- /dev/null
+++ b/emu/Nt/ie
@@ -1,0 +1,109 @@
+env
+ LDFLAGS= -subsystem:windows $LDFLAGS
+ OSX= ie-os
+
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ indir
+
+ draw ie-win
+
+ ip ipif ipaux
+ eia
+ audio audio
+ mem
+ pointer
+# arch
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+ 9
+
+link
+
+mod
+ sys
+ draw
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /root /
+ /root/dev /
+ /root/fd /
+ /root/prog /
+ /root/prof /
+ /root/net /
+ /root/net.alt /
+ /root/chan /
+ /root/nvfs /
+ /root/env /
+ /root/dis /
+ /root/dis/lib /
+ /root/dis/install /
+ /root/dis/install/archfs.dis /dis/install/archfs.dis
+ /root/dis/install/arch.dis /dis/install/arch.dis
+ /root/dis/gunzip.dis /dis/gunzip.dis
+ /root/dis/lib/bufio.dis /dis/lib/bufio.dis
+ /root/dis/lib/inflate.dis /dis/lib/inflate.dis
+ /root/dis/lib/string.dis /dis/lib/string.dis
+ /root/dis/lib/daytime.dis /dis/lib/daytime.dis
+ /root/dis/lib/arg.dis /dis/lib/arg.dis
+ /root/dis/lib/styx.dis /dis/lib/styx.dis
--- /dev/null
+++ b/emu/Nt/ie-os.c
@@ -1,0 +1,791 @@
+#define Unknown win_Unknown
+#define UNICODE
+#include <windows.h>
+#include <winbase.h>
+#include <winsock.h>
+#undef Unknown
+#include <excpt.h>
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "r16.h"
+#include "ieplugin.h"
+
+extern int SYS_SLEEP = 2;
+extern int SOCK_SELECT = 3;
+#define MAXSLEEPERS 1500
+
+extern Plugin* plugin;
+extern void newiop();
+extern int sendiop();
+
+DWORD PlatformId;
+static char* path;
+static HANDLE kbdh = INVALID_HANDLE_VALUE;
+static HANDLE conh = INVALID_HANDLE_VALUE;
+static int sleepers;
+
+static ulong erendezvous(void*, ulong);
+
+__declspec(thread) Proc *up;
+
+HANDLE ntfd2h(int);
+int nth2fd(HANDLE);
+char *hosttype = "Nt";
+
+static void
+pfree(Proc *p)
+{
+ Osenv *e;
+
+ lock(&procs.l);
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(p->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ e = p->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(e->user);
+ free(p->prog);
+ free(p);
+}
+
+void
+osblock(void)
+{
+ erendezvous(up, 0);
+}
+
+void
+osready(Proc *p)
+{
+ erendezvous(p, 0);
+}
+
+
+void
+pexit(char *msg, int t)
+{
+ pfree(up);
+ ExitThread(0);
+}
+
+LONG TrapHandler(LPEXCEPTION_POINTERS ureg);
+
+__cdecl
+Exhandler(EXCEPTION_RECORD *rec, void *frame, CONTEXT *context, void *dcon)
+{
+ EXCEPTION_POINTERS ep;
+ ep.ExceptionRecord = rec;
+ ep.ContextRecord = context;
+ TrapHandler(&ep);
+ return ExceptionContinueExecution;
+}
+
+DWORD WINAPI
+tramp(LPVOID p)
+{
+ // install our own exception handler
+ // replacing all others installed on this thread
+ DWORD handler = (DWORD)Exhandler;
+ _asm {
+ mov eax,handler
+ push eax
+ mov eax,-1
+ push eax
+ mov fs:[0],esp
+ }
+
+ up = p;
+ up->func(up->arg);
+ pexit("", 0);
+ // should never get here but tidy up anyway
+ _asm {
+ mov fs:[0],-1
+ add esp, 8
+ }
+ return 0;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ DWORD h;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+
+ p = newproc();
+ if(p == nil)
+ panic("out of kernel processes");
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->ui = up->env->ui;
+ kstrdup(&p->env->user, up->env->user);
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ p->pid = (int)CreateThread(0, 16384, tramp, p, 0, &h);
+ if(p->pid <= 0)
+ panic("ran out of kernel processes");
+}
+
+#if(_WIN32_WINNT >= 0x0400)
+void APIENTRY sleepintr(DWORD param)
+{
+}
+#endif
+
+void
+oshostintr(Proc *p)
+{
+ if (p->syscall == SOCK_SELECT)
+ return;
+ p->intwait = 0;
+#if(_WIN32_WINNT >= 0x0400)
+ if(p->syscall == SYS_SLEEP) {
+ QueueUserAPC(sleepintr, (HANDLE) p->pid, (DWORD) p->pid);
+ }
+#endif
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ longjmp(env, val);
+}
+
+int
+readkbd(void)
+{
+ DWORD r;
+ char buf[1];
+
+ if(ReadFile(plugin->conin, buf, sizeof(buf), &r, 0) == FALSE)
+ panic("keyboard fail");
+ if (r == 0)
+ panic("keyboard EOF");
+
+ if(buf[0] == '\r')
+ buf[0] = '\n';
+ return buf[0];
+}
+
+void
+cleanexit(int x)
+{
+ newiop();
+ IOP.op = Iquit;
+ sendiop();
+ ExitProcess(x);
+}
+
+struct ecodes {
+ DWORD code;
+ char* name;
+} ecodes[] = {
+ EXCEPTION_ACCESS_VIOLATION, "Segmentation violation",
+ EXCEPTION_DATATYPE_MISALIGNMENT, "Data Alignment",
+ EXCEPTION_BREAKPOINT, "Breakpoint",
+ EXCEPTION_SINGLE_STEP, "SingleStep",
+ EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array Bounds Check",
+ EXCEPTION_FLT_DENORMAL_OPERAND, "Denormalized Float",
+ EXCEPTION_FLT_DIVIDE_BY_ZERO, "Floating Point Divide by Zero",
+ EXCEPTION_FLT_INEXACT_RESULT, "Inexact Floating Point",
+ EXCEPTION_FLT_INVALID_OPERATION, "Invalid Floating Operation",
+ EXCEPTION_FLT_OVERFLOW, "Floating Point Result Overflow",
+ EXCEPTION_FLT_STACK_CHECK, "Floating Point Stack Check",
+ EXCEPTION_FLT_UNDERFLOW, "Floating Point Result Underflow",
+ EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by Zero",
+ EXCEPTION_INT_OVERFLOW, "Integer Overflow",
+ EXCEPTION_PRIV_INSTRUCTION, "Privileged Instruction",
+ EXCEPTION_IN_PAGE_ERROR, "Page-in Error",
+ EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal Instruction",
+ EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-Continuable Exception",
+ EXCEPTION_STACK_OVERFLOW, "Stack Overflow",
+ EXCEPTION_INVALID_DISPOSITION, "Invalid Disposition",
+ EXCEPTION_GUARD_PAGE, "Guard Page Violation",
+ 0, nil
+};
+
+void
+dodisfault(void)
+{
+ disfault(nil, up->env->errstr);
+}
+
+typedef struct Ereg Ereg;
+struct Ereg {
+ Ereg *prev;
+ FARPROC handler;
+};
+
+void
+dumpex()
+{
+ Ereg *er;
+ int i;
+ _asm { mov eax,fs:[0] };
+ _asm { mov [er],eax };
+
+ i = 0;
+ while ((unsigned)er != ~0) {
+ print("handler %ux\n", er->handler);
+ i++;
+ er = er->prev;
+ }
+ print("EXCEPTION CHAIN LENGTH = %d\n", i);
+}
+
+LONG
+TrapHandler(LPEXCEPTION_POINTERS ureg)
+{
+ int i;
+ char *name;
+ DWORD code;
+ // WORD pc;
+ char buf[ERRMAX];
+
+ code = ureg->ExceptionRecord->ExceptionCode;
+ // pc = ureg->ContextRecord->Eip;
+
+ name = nil;
+ for(i = 0; i < nelem(ecodes); i++) {
+ if(ecodes[i].code == code) {
+ name = ecodes[i].name;
+ break;
+ }
+ }
+
+ if(name == nil) {
+ snprint(buf, sizeof(buf), "Unrecognized Machine Trap (%.8lux)\n", code);
+ name = buf;
+ }
+/*
+ if(pc != 0) {
+ snprint(buf, sizeof(buf), "%s: pc=0x%lux", name, pc);
+ name = buf;
+ }
+*/
+ /* YUCK! */
+ strncpy(up->env->errstr, name, ERRMAX);
+ switch (code) {
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ case EXCEPTION_FLT_OVERFLOW:
+ case EXCEPTION_FLT_STACK_CHECK:
+ case EXCEPTION_FLT_UNDERFLOW:
+ /* clear exception flags and register stack */
+ _asm { fnclex };
+ ureg->ContextRecord->FloatSave.StatusWord = 0x0000;
+ ureg->ContextRecord->FloatSave.TagWord = 0xffff;
+ }
+ ureg->ContextRecord->Eip = (DWORD)dodisfault;
+ return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+static int rebootok = 0; /* is shutdown -r supported? */
+
+void
+osreboot(char *file, char **argv)
+{
+ if(rebootok){
+ execvp(file, argv);
+ panic("reboot failure");
+ }else
+ error("reboot option not supported on this system");
+}
+
+void
+libinit(char *imod)
+{
+ WSADATA wasdat;
+// DWORD lasterror, namelen;
+ OSVERSIONINFO os;
+// char sys[64];
+
+ os.dwOSVersionInfoSize = sizeof(os);
+ if(!GetVersionEx(&os))
+ panic("can't get os version");
+ PlatformId = os.dwPlatformId;
+ if (PlatformId == VER_PLATFORM_WIN32_NT) { /* true for NT and 2000 */
+ rebootok = 1;
+ } else {
+ rebootok = 0;
+ }
+
+ if((int)INVALID_HANDLE_VALUE != -1 || sizeof(HANDLE) != sizeof(int))
+ panic("invalid handle value or size");
+
+ if(WSAStartup(MAKEWORD(1, 1), &wasdat) != 0)
+ panic("no winsock.dll");
+
+// gethostname(sys, sizeof(sys));
+// kstrdup(&ossysname, sys);
+ kstrdup(&ossysname, "plugin");
+
+// if(sflag == 0)
+// SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)TrapHandler);
+
+ path = getenv("PATH");
+ if(path == nil)
+ path = ".";
+
+ up = newproc();
+ if(up == nil)
+ panic("cannot create kernel process");
+
+ kstrdup(&eve, "system");
+ emuinit(imod);
+}
+
+enum
+{
+ NHLOG = 7,
+ NHASH = (1<<NHLOG)
+};
+
+typedef struct Tag Tag;
+struct Tag
+{
+ void* tag;
+ ulong val;
+ HANDLE pid;
+ Tag* next;
+};
+
+static Tag* ht[NHASH];
+static Tag* ft;
+static Lock hlock;
+static int nsema;
+
+ulong
+erendezvous(void *tag, ulong value)
+{
+ int h;
+ ulong rval;
+ Tag *t, **l, *f;
+
+
+ h = (ulong)tag & (NHASH-1);
+
+ lock(&hlock);
+ l = &ht[h];
+ for(t = ht[h]; t; t = t->next) {
+ if(t->tag == tag) {
+ rval = t->val;
+ t->val = value;
+ t->tag = 0;
+ unlock(&hlock);
+ if(SetEvent(t->pid) == FALSE)
+ panic("Release failed\n");
+ return rval;
+ }
+ }
+
+ t = ft;
+ if(t == 0) {
+ t = malloc(sizeof(Tag));
+ if(t == nil)
+ panic("rendezvous: no memory");
+ t->pid = CreateEvent(0, 0, 0, 0);
+ }
+ else
+ ft = t->next;
+
+ t->tag = tag;
+ t->val = value;
+ t->next = *l;
+ *l = t;
+ unlock(&hlock);
+
+ if(WaitForSingleObject(t->pid, INFINITE) != WAIT_OBJECT_0)
+ panic("WaitForSingleObject failed\n");
+
+ lock(&hlock);
+ rval = t->val;
+ for(f = *l; f; f = f->next) {
+ if(f == t) {
+ *l = f->next;
+ break;
+ }
+ l = &f->next;
+ }
+ t->next = ft;
+ ft = t;
+ unlock(&hlock);
+
+ return rval;
+}
+
+void
+FPsave(void *fptr)
+{
+ _asm {
+ mov eax, fptr
+ fstenv [eax]
+ }
+}
+
+void
+FPrestore(void *fptr)
+{
+ _asm {
+ mov eax, fptr
+ fldenv [eax]
+ }
+}
+
+ulong
+umult(ulong a, ulong b, ulong *high)
+{
+ ulong lo, hi;
+
+ _asm {
+ mov eax, a
+ mov ecx, b
+ MUL ecx
+ mov lo, eax
+ mov hi, edx
+ }
+ *high = hi;
+ return lo;
+}
+
+int
+close(int fd)
+{
+ if(fd != -1)
+ CloseHandle(ntfd2h(fd));
+ return 0;
+}
+
+int
+read(int fd, void *buf, uint n)
+{
+ if(!ReadFile(ntfd2h(fd), buf, n, &n, NULL))
+ return -1;
+ return n;
+}
+
+int
+write(int fd, void *buf, uint n)
+{
+ if(fd == 1 || fd == 2){
+ int w;
+ if (plugin->conout == NULL)
+ return n;
+ if (!WriteFile(plugin->conout, buf, n, &w, NULL) || n != w)
+ abort();
+ return n;
+ }
+ if(!WriteFile(ntfd2h(fd), buf, n, &n, NULL))
+ return -1;
+ return n;
+}
+
+/*
+ * map handles and fds.
+ * this code assumes sizeof(HANDLE) == sizeof(int),
+ * that INVALID_HANDLE_VALUE is -1, and assumes
+ * that all tests of invalid fds check only for -1, not < 0
+ */
+int
+nth2fd(HANDLE h)
+{
+ return (int)h;
+}
+
+HANDLE
+ntfd2h(int fd)
+{
+ return (HANDLE)fd;
+}
+
+void
+oslopri(void)
+{
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
+}
+
+/* Resolve system header name conflict */
+#undef Sleep
+void
+sleep(int secs)
+{
+ Sleep(secs*1000);
+}
+
+void*
+sbrk(int size)
+{
+ void *brk;
+
+ brk = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ if(brk == 0)
+ return (void*)-1;
+
+ return brk;
+}
+
+ulong
+getcallerpc(void *arg)
+{
+ ulong cpc;
+ _asm {
+ mov eax, dword ptr [ebp]
+ mov eax, dword ptr [eax+4]
+ mov dword ptr cpc, eax
+ }
+ return cpc;
+}
+
+/*
+ulong
+getpc(void *arg, ulong *narg)
+{
+ ulong *a = arg, *fp, pc;
+
+ if(a == nil){
+ *narg = 0;
+ return 0;
+ }
+ fp = a-2;
+ pc = fp[1];
+ fp = *(ulong**)fp;
+ if(fp == nil)
+ *narg = 0;
+ else
+ *narg = (ulong)(fp+2);
+ return pc;
+}
+*/
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ return GetTickCount();
+}
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ * days per month plus days/year
+ */
+static int dmsize[] =
+{
+ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static int ldmsize[] =
+{
+ 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * return the days/month for the given year
+ */
+static int*
+yrsize(int yr)
+{
+ /* a leap year is a multiple of 4, excluding centuries
+ * that are not multiples of 400 */
+ if( (yr % 4 == 0) && (yr % 100 != 0 || yr % 400 == 0) )
+ return ldmsize;
+ else
+ return dmsize;
+}
+
+static long
+tm2sec(SYSTEMTIME *tm)
+{
+ long secs;
+ int i, *d2m;
+
+ secs = 0;
+
+ /*
+ * seconds per year
+ */
+ for(i = 1970; i < tm->wYear; i++){
+ d2m = yrsize(i);
+ secs += d2m[0] * SEC2DAY;
+ }
+
+ /*
+ * seconds per month
+ */
+ d2m = yrsize(tm->wYear);
+ for(i = 1; i < tm->wMonth; i++)
+ secs += d2m[i] * SEC2DAY;
+
+ /*
+ * secs in last month
+ */
+ secs += (tm->wDay-1) * SEC2DAY;
+
+ /*
+ * hours, minutes, seconds
+ */
+ secs += tm->wHour * SEC2HOUR;
+ secs += tm->wMinute * SEC2MIN;
+ secs += tm->wSecond;
+
+ return secs;
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osusectime(void)
+{
+ SYSTEMTIME tm;
+ vlong secs;
+
+ GetSystemTime(&tm);
+ secs = tm2sec(&tm);
+ return secs * 1000000 + tm.wMilliseconds * 1000;
+}
+
+vlong
+osnsec(void)
+{
+ return osusectime()*1000; /* TO DO better */
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ up->syscall = 1;
+ SleepEx(milsec, FALSE);
+ up->syscall = 0;
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ if (sleepers > MAXSLEEPERS)
+ return -1;
+ sleepers++;
+ up->syscall = SYS_SLEEP;
+ SleepEx(milsec, TRUE);
+ up->syscall = 0;
+ sleepers--;
+ return 0;
+}
+
+void
+osyield(void)
+{
+ sleep(0);
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ sleep(1000000);
+}
+
+/*
+ * these should never be called, and are included
+ * as stubs since we are linking against a library which defines them
+ */
+int
+open(const char *path, int how, ...)
+{
+ panic("open");
+ return -1;
+}
+
+int
+creat(const char *path, int how)
+{
+ panic("creat");
+ return -1;
+}
+
+int
+stat(const char *path, struct stat *sp)
+{
+ panic("stat");
+ return -1;
+}
+
+int
+chown(const char *path, int uid, int gid)
+{
+ panic("chown");
+ return -1;
+}
+
+int
+chmod(const char *path, int mode)
+{
+ panic("chmod");
+ return -1;
+}
+
+void
+link(char *path, char *next)
+{
+ panic("link");
+}
+
+int
+segflush(void *a, ulong n)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/Nt/ie-win.c
@@ -1,0 +1,224 @@
+#define Unknown win_Unknown
+#include <windows.h>
+#undef Unknown
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include "keyboard.h"
+#include "cursor.h"
+#include "ieplugin.h"
+
+/*
+ * image channel descriptors - copied from draw.h as it clashes with windows.h on many things
+ */
+enum {
+ CRed = 0,
+ CGreen,
+ CBlue,
+ CGrey,
+ CAlpha,
+ CMap,
+ CIgnore,
+ NChan,
+};
+
+#define __DC(type, nbits) ((((type)&15)<<4)|((nbits)&15))
+#define CHAN1(a,b) __DC(a,b)
+#define CHAN2(a,b,c,d) (CHAN1((a),(b))<<8|__DC((c),(d)))
+#define CHAN3(a,b,c,d,e,f) (CHAN2((a),(b),(c),(d))<<8|__DC((e),(f)))
+#define CHAN4(a,b,c,d,e,f,g,h) (CHAN3((a),(b),(c),(d),(e),(f))<<8|__DC((g),(h)))
+
+#define NBITS(c) ((c)&15)
+#define TYPE(c) (((c)>>4)&15)
+
+enum {
+ GREY1 = CHAN1(CGrey, 1),
+ GREY2 = CHAN1(CGrey, 2),
+ GREY4 = CHAN1(CGrey, 4),
+ GREY8 = CHAN1(CGrey, 8),
+ CMAP8 = CHAN1(CMap, 8),
+ RGB15 = CHAN4(CIgnore, 1, CRed, 5, CGreen, 5, CBlue, 5),
+ RGB16 = CHAN3(CRed, 5, CGreen, 6, CBlue, 5),
+ RGB24 = CHAN3(CRed, 8, CGreen, 8, CBlue, 8),
+ RGBA32 = CHAN4(CRed, 8, CGreen, 8, CBlue, 8, CAlpha, 8),
+ ARGB32 = CHAN4(CAlpha, 8, CRed, 8, CGreen, 8, CBlue, 8), /* stupid VGAs */
+ XRGB32 = CHAN4(CIgnore, 8, CRed, 8, CGreen, 8, CBlue, 8),
+};
+
+extern ulong displaychan;
+
+extern void drawend(void);
+
+extern int chantodepth(ulong);
+extern int main(int argc, char **argv);
+static void dprint(char*, ...);
+static DWORD WINAPI winproc(LPVOID);
+
+static HINSTANCE inst;
+static HINSTANCE previnst;
+static int attached;
+static ulong *data;
+
+extern DWORD PlatformId;
+char* gkscanid = "emu_win32vk";
+
+extern int cflag;
+Plugin *plugin = NULL;
+
+DWORD WINAPI
+pluginproc(LPVOID p)
+{
+ int x, y, b;
+
+ for (;;) {
+ WaitForSingleObject(plugin->dopop, INFINITE);
+ switch (POP.op) {
+ case Pgfxkey:
+ if(gkbdq != nil)
+ gkbdputc(gkbdq, POP.u.key);
+ break;
+ case Pmouse:
+ x = POP.u.m.x;
+ y = POP.u.m.y;
+ b = POP.u.m.b;
+ mousetrack(b, x, y, 0);
+ break;
+ }
+ SetEvent(plugin->popdone);
+ }
+}
+
+int WINAPI
+WinMain(HINSTANCE winst, HINSTANCE wprevinst, LPSTR cmdline, int wcmdshow)
+{
+ HANDLE sharedmem;
+ uint pid = _getpid();
+ char iname[16];
+ inst = winst;
+ previnst = wprevinst;
+ sprint(iname, "%uX", pid);
+ sharedmem = OpenFileMapping(FILE_MAP_WRITE, FALSE, iname);
+ if (sharedmem != NULL)
+ plugin = MapViewOfFile(sharedmem, FILE_MAP_WRITE, 0, 0, 0);
+ if (plugin != NULL) {
+ DWORD tid;
+ int i;
+ Xsize = plugin->Xsize;
+ Ysize = plugin->Ysize;
+ displaychan = plugin->cdesc;
+ cflag = plugin->cflag;
+ for (i = 0; i < PI_NCLOSE; i++)
+ CloseHandle(plugin->closehandles[i]);
+ CreateThread(0, 0, pluginproc, 0, 0, &tid);
+
+ /* cmdline passed into WinMain does not contain name of executable.
+ * The globals __argc and __argv to include this info - like UNIX
+ */
+ main(__argc, __argv);
+ UnmapViewOfFile(plugin);
+ plugin = NULL;
+ }
+ if (sharedmem != NULL)
+ CloseHandle(sharedmem);
+ return 0;
+}
+
+static Lock ioplock;
+
+void
+newiop()
+{
+ lock(&ioplock);
+}
+
+int
+sendiop()
+{
+ int val;
+ SetEvent(plugin->doiop);
+ WaitForSingleObject(plugin->iopdone, INFINITE);
+ val = plugin->iop.val;
+ unlock(&ioplock);
+ return val;
+}
+
+void
+dprint(char *fmt, ...)
+{
+ va_list arg;
+ char buf[128];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, (LPSTR)arg);
+ va_end(arg);
+ OutputDebugString("inferno: ");
+ OutputDebugString(buf);
+}
+
+uchar*
+attachscreen(IRectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+ int k;
+
+ if (!attached) {
+ newiop();
+ IOP.op = Iattachscr;
+ if (sendiop() != 0)
+ return nil;
+ data = plugin->screen;
+ attached = 1;
+ }
+ r->min.x = 0;
+ r->min.y = 0;
+ r->max.x = Xsize;
+ r->max.y = Ysize;
+
+ if(displaychan == 0)
+ displaychan = CMAP8;
+ *chan = displaychan;
+
+ k = chantodepth(displaychan);
+ *d = k;
+ *width = (Xsize/4)*(k/8);
+ *softscreen = 1;
+ return (uchar*)data;
+}
+
+void
+flushmemscreen(IRectangle r)
+{
+ if(r.max.x<=r.min.x || r.max.y<=r.min.y)
+ return;
+ newiop();
+ IOP.op = Iflushscr;
+ IOP.u.r = r;
+ sendiop();
+}
+
+void
+setpointer(int x, int y)
+{
+ USED(x); USED(y);
+ // TODO
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+ USED(c);
+ // TODO
+}
+
+char*
+clipread(void)
+{
+ return nil;
+}
+
+int
+clipwrite(char *p)
+{
+ USED(p);
+ return -1;
+}
--- /dev/null
+++ b/emu/Nt/ieplugin.h
@@ -1,0 +1,75 @@
+typedef struct Pop Pop;
+typedef struct Iop Iop;
+typedef struct IPoint IPoint;
+typedef struct IRectangle IRectangle;
+typedef struct Plugin Plugin;
+
+enum {
+ // messages from Plugin to Inferno
+ Pgfxkey,
+ Pmouse,
+
+ // message from Inferno to Plugin
+ Iattachscr,
+ Iflushscr,
+ Isetcur,
+ Idrawcur,
+ Iquit,
+};
+
+struct Pop {
+ int op;
+ union {
+ int key; // Pgfxkey
+ struct {
+ int x;
+ int y;
+ int b;
+ int modify;
+ } m; // Pmouse
+ } u;
+};
+
+struct IPoint
+{
+ LONG x;
+ LONG y;
+};
+
+struct IRectangle
+{
+ IPoint min;
+ IPoint max;
+};
+
+struct Iop {
+ int op;
+ int val;
+ union {
+ IRectangle r; // Iflushscr
+ // need additional support for Isetcur & Idrawcur
+ } u;
+};
+#define PI_NCLOSE 2
+
+struct Plugin {
+ LONG sz; // size of this data struct (including screen mem)
+ HANDLE conin; // console input (from plugin) - never NULL
+ HANDLE conout; // console output (to plugin) - can be NULL
+ HANDLE datain; // #C data file for initialisation (HACK!)
+ HANDLE dopop; // new Pop available
+ HANDLE popdone; // acknowledgement of Pop
+ HANDLE doiop; // new Iop available
+ HANDLE iopdone; // acknowledgement of Iop
+ HANDLE closehandles[PI_NCLOSE];
+ Pop pop;
+ Iop iop;
+ int Xsize; // screen dimensions
+ int Ysize;
+ ULONG cdesc; // display chans descriptor
+ int cflag;
+ ULONG screen[1];
+};
+
+#define IOP (plugin->iop)
+#define POP (plugin->pop)
--- /dev/null
+++ b/emu/Nt/ipif.c
@@ -1,0 +1,409 @@
+#define Unknown win_Unknown
+#include <windows.h>
+#include <winbase.h>
+#include <sys/types.h>
+#include <winsock.h>
+#undef Unknown
+#include "dat.h"
+#include "fns.h"
+#include "ip.h"
+#include "error.h"
+
+typedef int socklen_t; /* Windows is leading edge as always */
+
+
+extern int SOCK_SELECT;
+
+char Enotv4[] = "address not IPv4";
+
+static void
+ipw6(uchar *a, ulong w)
+{
+ memmove(a, v4prefix, IPv4off);
+ memmove(a+IPv4off, &w, IPv4addrlen);
+}
+
+int
+so_socket(int type)
+{
+ int fd, one;
+
+ switch(type) {
+ default:
+ error("bad protocol type");
+ case S_TCP:
+ type = SOCK_STREAM;
+ break;
+ case S_UDP:
+ type = SOCK_DGRAM;
+ break;
+ }
+ fd = socket(AF_INET, type, 0);
+ if(fd < 0)
+ oserror();
+ if(type == SOCK_DGRAM){
+ one = 1;
+ setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one));
+ }else{
+ one = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one));
+ }
+ return fd;
+}
+
+int
+so_send(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+ int r;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+ uchar *h = hdr;
+
+
+ osenter();
+ if(hdr == 0)
+ r = send(sock, va, len, 0);
+ else {
+ memset(&sa, 0, sizeof(sa));
+ sin = (struct sockaddr_in*)&sa;
+ sin->sin_family = AF_INET;
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ memmove(&sin->sin_addr, h, 4);
+ memmove(&sin->sin_port, h+8, 2);
+ break;
+ case OUdphdrlen:
+ v6tov4((uchar*)&sin->sin_addr, h);
+ memmove(&sin->sin_port, h+2*IPaddrlen, 2); /* rport */
+ break;
+ default:
+ v6tov4((uchar*)&sin->sin_addr, h);
+ memmove(&sin->sin_port, h+3*IPaddrlen, 2);
+ break;
+ }
+ r = sendto(sock, va, len, 0, &sa, sizeof(sa));
+ }
+ osleave();
+ return r;
+}
+
+static int
+doselect(int sock)
+{
+ fd_set waitr;
+ struct timeval seltime;
+
+ up->syscall = SOCK_SELECT;
+ FD_ZERO(&waitr);
+ FD_SET(sock, &waitr);
+ for(;;){
+ int nfds;
+ fd_set in, exc;
+
+ in = waitr;
+ exc = waitr;
+ seltime.tv_sec = 1;
+ seltime.tv_usec = 0L;
+ nfds = select(sizeof(fd_set)*8, &in, (fd_set*)0, &exc, &seltime);
+ if(up->intwait) {
+ up->intwait = 0;
+ return -1;
+ }
+ if(nfds < 0) {
+ print("select error\n");
+ return 0;
+ }
+ if(FD_ISSET(sock, &in) || FD_ISSET(sock, &exc)){
+ return 0;
+ }
+ }
+}
+
+int
+so_recv(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+ int r;
+ socklen_t l;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+ uchar h[Udphdrlen];
+
+ osenter();
+ if(doselect(sock) < 0) {
+ osleave();
+ return -1;
+ }
+ if(hdr == 0)
+ r = recv(sock, va, len, 0);
+ else {
+ sin = (struct sockaddr_in*)&sa;
+ l = sizeof(sa);
+ r = recvfrom(sock, va, len, 0, &sa, &l);
+ if(r >= 0) {
+ memset(h, 0, sizeof(h));
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ memmove(h, &sin->sin_addr, IPv4addrlen);
+ memmove(h+2*IPv4addrlen, &sin->sin_port, 2);
+ break;
+ case OUdphdrlen:
+ v4tov6(h, (uchar*)&sin->sin_addr);
+ memmove(h+2*IPaddrlen, &sin->sin_port, 2);
+ break;
+ default:
+ v4tov6(h, (uchar*)&sin->sin_addr);
+ memmove(h+3*IPaddrlen, &sin->sin_port, 2);
+ break;
+ }
+
+ /* alas there's no way to get the local addr/port correctly. Pretend. */
+ memset(&sa, 0, sizeof(sa));
+ getsockname(sock, &sa, &l);
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen);
+ memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2);
+ break;
+ case OUdphdrlen:
+ v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr);
+ memmove(h+2*IPaddrlen+2, &sin->sin_port, 2);
+ break;
+ default:
+ v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr);
+ v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr); /* ifcaddr */
+ memmove(h+3*IPaddrlen+2, &sin->sin_port, 2);
+ break;
+ }
+ memmove(hdr, h, hdrlen);
+ }
+ }
+ osleave();
+ return r;
+}
+
+void
+so_close(int sock)
+{
+ closesocket(sock);
+}
+
+void
+so_connect(int fd, uchar *raddr, ushort rport)
+{
+ int r;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ if(!isv4(raddr))
+ error(Enotv4);
+
+ memset(&sa, 0, sizeof(sa));
+ sin = (struct sockaddr_in*)&sa;
+ sin->sin_family = AF_INET;
+ hnputs(&sin->sin_port, rport);
+ memmove(&sin->sin_addr.s_addr, raddr+IPv4off, IPv4addrlen);
+
+ osenter();
+ r = connect(fd, &sa, sizeof(sa));
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+void
+so_getsockname(int fd, uchar *laddr, ushort *lport)
+{
+ socklen_t len;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ len = sizeof(sa);
+ if(getsockname(fd, &sa, &len) < 0)
+ oserror();
+
+ sin = (struct sockaddr_in*)&sa;
+ if(sin->sin_family != AF_INET || len != sizeof(*sin))
+ error(Enotv4);
+
+ ipw6(laddr, sin->sin_addr.s_addr);
+ *lport = nhgets(&sin->sin_port);
+}
+
+void
+so_listen(int fd)
+{
+ int r;
+
+ osenter();
+ r = listen(fd, 256);
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+int
+so_accept(int fd, uchar *raddr, ushort *rport)
+{
+ int nfd;
+ socklen_t len;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in*)&sa;
+
+ len = sizeof(sa);
+ osenter();
+ if(doselect(fd) < 0) {
+ osleave();
+ return -1;
+ }
+ nfd = accept(fd, &sa, &len);
+ osleave();
+ if(nfd < 0)
+ oserror();
+
+ if(sin->sin_family != AF_INET || len != sizeof(*sin))
+ error(Enotv4);
+
+ ipw6(raddr, sin->sin_addr.s_addr);
+ *rport = nhgets(&sin->sin_port);
+ return nfd;
+}
+
+void
+so_bind(int fd, int su, uchar *addr, ushort port)
+{
+ int i, one;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in*)&sa;
+
+ one = 1;
+// if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) {
+// oserrstr(up->genbuf, sizeof(up->genbuf));
+// print("setsockopt: %s", err);
+// }
+
+ if(su) {
+ for(i = 600; i < 1024; i++) {
+ memset(&sa, 0, sizeof(sa));
+ sin->sin_family = AF_INET;
+ memmove(&sin->sin_addr.s_addr, addr+IPv4off, IPv4addrlen);
+ hnputs(&sin->sin_port, i);
+
+ if(bind(fd, &sa, sizeof(sa)) >= 0)
+ return;
+ }
+ oserror();
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sin->sin_family = AF_INET;
+ memmove(&sin->sin_addr.s_addr, addr+IPv4off, IPv4addrlen);
+ hnputs(&sin->sin_port, port);
+
+ if(bind(fd, &sa, sizeof(sa)) < 0)
+ oserror();
+}
+
+int
+so_gethostbyname(char *host, char**hostv, int n)
+{
+ int i;
+ char buf[32];
+ uchar *p;
+ struct hostent *hp;
+
+ hp = gethostbyname(host);
+ if(hp == 0)
+ return 0;
+
+ for(i = 0; hp->h_addr_list[i] && i < n; i++) {
+ p = (uchar*)hp->h_addr_list[i];
+ sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]);
+ hostv[i] = strdup(buf);
+ if(hostv[i] == 0)
+ break;
+ }
+ return i;
+}
+
+int
+so_gethostbyaddr(char *addr, char **hostv, int n)
+{
+ int i;
+ struct hostent *hp;
+ unsigned long straddr;
+
+ straddr = inet_addr(addr);
+ if(straddr == -1)
+ return 0;
+
+ hp = gethostbyaddr((char *)&straddr, sizeof(straddr), AF_INET);
+ if(hp == 0)
+ return 0;
+
+ hostv[0] = strdup(hp->h_name);
+ if(hostv[0] == 0)
+ return 0;
+ for(i = 1; hp->h_aliases[i-1] && i < n; i++) {
+ hostv[i] = strdup(hp->h_aliases[i-1]);
+ if(hostv[i] == 0)
+ break;
+ }
+ return i;
+}
+
+int
+so_getservbyname(char *service, char *net, char *port)
+{
+ ushort p;
+ struct servent *s;
+
+ s = getservbyname(service, net);
+ if(s == 0)
+ return -1;
+ p = s->s_port;
+ sprint(port, "%d", nhgets(&p));
+ return 0;
+}
+
+int
+so_hangup(int fd, int nolinger)
+{
+ int r;
+ static struct linger l = {1, 0};
+
+ osenter();
+ if(nolinger)
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
+ r = closesocket(fd);
+ osleave();
+ return r;
+}
+
+void
+arpadd(char *ipaddr, char *eaddr, int n)
+{
+ error("arp not implemented");
+}
+
+int
+so_mustbind(int restricted, int port)
+{
+ USED(restricted);
+ USED(port);
+ /* Windows requires bound sockets, even on port 0 */
+ return 1;
+}
+
+void
+so_keepalive(int fd, int ms)
+{
+ int on;
+
+ USED(ms);
+ on = 1;
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
+}
--- /dev/null
+++ b/emu/Nt/ipif6.c
@@ -1,0 +1,413 @@
+#define Unknown win_Unknown
+
+/* vista (0x0600) was the first to support IPPROTO_IPV6 */
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+
+#include <winsock2.h>
+#include <ws2def.h>
+#include <ws2tcpip.h>
+#include <wspiapi.h>
+#undef Unknown
+
+#include "dat.h"
+#include "fns.h"
+#include "ip.h"
+#include "error.h"
+
+extern int SOCK_SELECT;
+
+int
+so_socket(int type)
+{
+ int fd, one;
+ int v6only;
+
+ switch(type) {
+ default:
+ error("bad protocol type");
+ case S_TCP:
+ type = SOCK_STREAM;
+ break;
+ case S_UDP:
+ type = SOCK_DGRAM;
+ break;
+ }
+
+ fd = socket(AF_INET6, type, 0);
+ if(fd < 0)
+ oserror();
+
+ /* OpenBSD has v6only fixed to 1, so the following will fail */
+ v6only = 0;
+ if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&v6only, sizeof v6only) < 0)
+ oserror();
+
+ if(type == SOCK_DGRAM){
+ one = 1;
+ setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one));
+ }else{
+ one = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one));
+ }
+ return fd;
+}
+
+int
+so_send(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+ int r;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+ char *h = hdr;
+
+ osenter();
+ if(hdr == 0)
+ r = send(sock, va, len, 0);
+ else {
+ memset(&sa, 0, sizeof(sa));
+ sin6 = (struct sockaddr_in6*)&sa;
+ sin6->sin6_family = AF_INET6;
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ v4tov6((uchar*)&sin6->sin6_addr, h);
+ memmove(&sin6->sin6_port, h+8, 2);
+ break;
+ case OUdphdrlen:
+ memmove((uchar*)&sin6->sin6_addr, h, IPaddrlen);
+ memmove(&sin6->sin6_port, h+2*IPaddrlen, 2); /* rport */
+ break;
+ default:
+ memmove((uchar*)&sin6->sin6_addr, h, IPaddrlen);
+ memmove(&sin6->sin6_port, h+3*IPaddrlen, 2);
+ break;
+ }
+ r = sendto(sock, va, len, 0, (struct sockaddr*)sin6, sizeof(*sin6));
+ }
+ osleave();
+ return r;
+}
+
+static int
+doselect(int sock)
+{
+ fd_set waitr;
+ struct timeval seltime;
+
+ up->syscall = SOCK_SELECT;
+ FD_ZERO(&waitr);
+ FD_SET(sock, &waitr);
+ for(;;){
+ int nfds;
+ fd_set in, exc;
+
+ in = waitr;
+ exc = waitr;
+ seltime.tv_sec = 1;
+ seltime.tv_usec = 0L;
+ nfds = select(sizeof(fd_set)*8, &in, (fd_set*)0, &exc, &seltime);
+ if(up->intwait) {
+ up->intwait = 0;
+ return -1;
+ }
+ if(nfds < 0) {
+ print("select error\n");
+ return 0;
+ }
+ if(FD_ISSET(sock, &in) || FD_ISSET(sock, &exc)){
+ return 0;
+ }
+ }
+}
+
+int
+so_recv(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+ int r, l;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+ char h[Udphdrlen];
+
+ osenter();
+ if(doselect(sock) < 0) {
+ osleave();
+ return -1;
+ }
+ if(hdr == 0)
+ r = recv(sock, va, len, 0);
+ else {
+ sin6 = (struct sockaddr_in6*)&sa;
+ l = sizeof(sa);
+ r = recvfrom(sock, va, len, 0, (struct sockaddr*)&sa, &l);
+ if(r >= 0) {
+ memset(h, 0, sizeof h);
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ if(v6tov4(h, (uchar*)&sin6->sin6_addr) < 0) {
+ osleave();
+ error("OUdphdrlenv4 with IPv6 address");
+ }
+ memmove(h+2*IPv4addrlen, &sin6->sin6_port, 2);
+ break;
+ case OUdphdrlen:
+ memmove(h, (uchar*)&sin6->sin6_addr, IPaddrlen);
+ memmove(h+2*IPaddrlen, &sin6->sin6_port, 2);
+ break;
+ default:
+ memmove(h, (uchar*)&sin6->sin6_addr, IPaddrlen);
+ memmove(h+3*IPaddrlen, &sin6->sin6_port, 2);
+ break;
+ }
+
+ /* alas there's no way to get the local addr/port correctly. Pretend. */
+ memset(&sa, 0, sizeof sa);
+ l = sizeof(sa);
+ getsockname(sock, (struct sockaddr*)&sa, &l);
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ /*
+ * we get v6Unspecified/noaddr if local address cannot be determined.
+ * that's reasonable for ipv4 too.
+ */
+ if(ipcmp(v6Unspecified, (uchar*)&sin6->sin6_addr) != 0
+ && v6tov4(h+IPv4addrlen, (uchar*)&sin6->sin6_addr) < 0) {
+ osleave();
+ error("OUdphdrlenv4 with IPv6 address");
+ }
+ memmove(h+2*IPv4addrlen+2, &sin6->sin6_port, 2);
+ break;
+ case OUdphdrlen:
+ memmove(h+IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen);
+ memmove(h+2*IPaddrlen+2, &sin6->sin6_port, 2);
+ break;
+ default:
+ memmove(h+IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen);
+ memmove(h+2*IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen); /* ifcaddr */
+ memmove(h+3*IPaddrlen+2, &sin6->sin6_port, 2);
+ break;
+ }
+ memmove(hdr, h, hdrlen);
+ }
+ }
+ osleave();
+ return r;
+}
+
+void
+so_close(int sock)
+{
+ closesocket(sock);
+}
+
+void
+so_connect(int fd, unsigned char *raddr, unsigned short rport)
+{
+ int r;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+
+ memset(&sa, 0, sizeof(sa));
+ sin6 = (struct sockaddr_in6*)&sa;
+ sin6->sin6_family = AF_INET6;
+ hnputs(&sin6->sin6_port, rport);
+ memmove((uchar*)&sin6->sin6_addr, raddr, IPaddrlen);
+
+ osenter();
+ r = connect(fd, (struct sockaddr*)sin6, sizeof(*sin6));
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+void
+so_getsockname(int fd, unsigned char *laddr, unsigned short *lport)
+{
+ int len;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+
+ len = sizeof(*sin6);
+ if(getsockname(fd, (struct sockaddr*)&sa, &len) < 0)
+ oserror();
+
+ sin6 = (struct sockaddr_in6*)&sa;
+ if(sin6->sin6_family != AF_INET6 || len != sizeof(*sin6))
+ error("not AF_INET6");
+
+ memmove(laddr, &sin6->sin6_addr, IPaddrlen);
+ *lport = nhgets(&sin6->sin6_port);
+}
+
+void
+so_listen(int fd)
+{
+ int r;
+
+ osenter();
+ r = listen(fd, 256);
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+int
+so_accept(int fd, unsigned char *raddr, unsigned short *rport)
+{
+ int nfd, len;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6*)&sa;
+
+ len = sizeof(*sin6);
+ osenter();
+ if(doselect(fd) < 0) {
+ osleave();
+ return -1;
+ }
+ nfd = accept(fd, (struct sockaddr*)&sa, &len);
+ osleave();
+ if(nfd < 0)
+ oserror();
+
+ if(sin6->sin6_family != AF_INET6 || len != sizeof(*sin6))
+ error("not AF_INET6");
+
+ memmove(raddr, &sin6->sin6_addr, IPaddrlen);
+ *rport = nhgets(&sin6->sin6_port);
+ return nfd;
+}
+
+void
+so_bind(int fd, int su, unsigned char *addr, unsigned short port)
+{
+ int i, one;
+ struct sockaddr_storage sa;
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6*)&sa;
+
+ one = 1;
+ if(0 && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) {
+ oserrstr(up->genbuf, sizeof(up->genbuf));
+ print("setsockopt: %s", up->genbuf);
+ }
+
+ if(su) {
+ for(i = 600; i < 1024; i++) {
+ memset(&sa, 0, sizeof(sa));
+ sin6->sin6_family = AF_INET6;
+ memmove(&sin6->sin6_addr, addr, IPaddrlen);
+ hnputs(&sin6->sin6_port, i);
+
+ if(bind(fd, (struct sockaddr*)sin6, sizeof(*sin6)) >= 0)
+ return;
+ }
+ oserror();
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sin6->sin6_family = AF_INET6;
+ memmove(&sin6->sin6_addr, addr, IPaddrlen);
+ hnputs(&sin6->sin6_port, port);
+
+ if(bind(fd, (struct sockaddr*)sin6, sizeof(*sin6)) < 0)
+ oserror();
+}
+
+static int
+resolve(char *name, char **hostv, int n, int isnumeric)
+{
+ int i;
+ struct addrinfo *res0, *r;
+ char buf[5*8];
+ uchar addr[IPaddrlen];
+ struct addrinfo hints;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = isnumeric ? AI_NUMERICHOST : 0;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if(getaddrinfo(name, nil, &hints, &res0) < 0)
+ return 0;
+ i = 0;
+ for(r = res0; r != nil && i < n; r = r->ai_next) {
+ if(r->ai_family == AF_INET)
+ v4tov6(addr, (uchar*)&((struct sockaddr_in*)r->ai_addr)->sin_addr);
+ else if(r->ai_family == AF_INET6)
+ memmove(addr, &((struct sockaddr_in6*)r->ai_addr)->sin6_addr, IPaddrlen);
+ else
+ continue;
+
+ snprint(buf, sizeof buf, "%I", addr);
+ hostv[i++] = strdup(buf);
+ }
+
+ freeaddrinfo(res0);
+ return i;
+}
+
+int
+so_gethostbyname(char *host, char **hostv, int n)
+{
+ return resolve(host, hostv, n, 0);
+}
+
+int
+so_gethostbyaddr(char *addr, char **hostv, int n)
+{
+ return resolve(addr, hostv, n, 1);
+}
+
+int
+so_getservbyname(char *service, char *net, char *port)
+{
+ ushort p;
+ struct servent *s;
+
+ s = getservbyname(service, net);
+ if(s == 0)
+ return -1;
+ p = s->s_port;
+ sprint(port, "%d", nhgets(&p));
+ return 0;
+}
+
+int
+so_hangup(int fd, int nolinger)
+{
+ int r;
+ static struct linger l = {1, 0};
+
+ osenter();
+ if(nolinger)
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
+ r = closesocket(fd);
+ osleave();
+ return r;
+}
+
+void
+arpadd(char *ipaddr, char *eaddr, int n)
+{
+ error("arp not implemented");
+}
+
+int
+so_mustbind(int restricted, int port)
+{
+ USED(restricted);
+ USED(port);
+ /* Windows requires bound sockets, even on port 0 */
+ return 1;
+}
+
+void
+so_keepalive(int fd, int ms)
+{
+ int on;
+
+ USED(ms);
+ on = 1;
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
+}
--- /dev/null
+++ b/emu/Nt/mkfile
@@ -1,0 +1,51 @@
+SYSTARG=Nt
+OBJTYPE=386
+#uncomment following line for full Microsoft debug symbols
+#LDEBUG=-debug -debugtype:cv -pdb:none
+<../../mkconfig
+SYSTARG=Nt
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu ie
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+OSX=os
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ $OSX.$O\
+ $CONF.root.$O\
+ lock.$O\
+ fp.$O\
+ r16.$O\
+ vlrt.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+ r16.h\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= $SYSLIBS netapi32.lib wsock32.lib user32.lib gdi32.lib advapi32.lib winmm.lib mpr.lib
+KERNDATE=`{$NDATE}
+
+default:V: i$CONF.exe
+
+<../port/portmkfile
+
+i$CONF.exe: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -out:$target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: i$CONF.exe
+ cp i$CONF.exe $INSTALLDIR/$CONF.exe
--- /dev/null
+++ b/emu/Nt/nt.rc
@@ -1,0 +1,1 @@
+100 ICON inferno.ico
--- /dev/null
+++ b/emu/Nt/os.c
@@ -1,0 +1,715 @@
+#define Unknown win_Unknown
+#define UNICODE
+#include <windows.h>
+#include <winbase.h>
+#include <winsock.h>
+#undef Unknown
+#include <excpt.h>
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include "r16.h"
+
+int SYS_SLEEP = 2;
+int SOCK_SELECT = 3;
+#define MAXSLEEPERS 1500
+
+extern int cflag;
+
+DWORD PlatformId;
+DWORD consolestate;
+static char* path;
+static HANDLE kbdh = INVALID_HANDLE_VALUE;
+static HANDLE conh = INVALID_HANDLE_VALUE;
+static HANDLE errh = INVALID_HANDLE_VALUE;
+static int donetermset = 0;
+static int sleepers = 0;
+
+
+__declspec(thread) Proc *up;
+
+HANDLE ntfd2h(int);
+int nth2fd(HANDLE);
+void termrestore(void);
+char *hosttype = "Nt";
+char *cputype = "386";
+void (*coherence)(void) = nofence;
+
+static void
+pfree(Proc *p)
+{
+ Osenv *e;
+
+ lock(&procs.l);
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(p->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ e = p->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(e->user);
+ free(p->prog);
+ CloseHandle((HANDLE)p->os);
+ free(p);
+}
+
+void
+osblock(void)
+{
+ if(WaitForSingleObject((HANDLE)up->os, INFINITE) != WAIT_OBJECT_0)
+ panic("osblock failed");
+}
+
+void
+osready(Proc *p)
+{
+ if(SetEvent((HANDLE)p->os) == FALSE)
+ panic("osready failed");
+}
+
+void
+pexit(char *msg, int t)
+{
+ pfree(up);
+ ExitThread(0);
+}
+
+LONG TrapHandler(LPEXCEPTION_POINTERS ureg);
+
+__cdecl
+Exhandler(EXCEPTION_RECORD *rec, void *frame, CONTEXT *context, void *dcon)
+{
+ EXCEPTION_POINTERS ep;
+ ep.ExceptionRecord = rec;
+ ep.ContextRecord = context;
+ TrapHandler(&ep);
+ return ExceptionContinueExecution;
+}
+
+DWORD WINAPI
+tramp(LPVOID p)
+{
+ up = p;
+ up->func(up->arg);
+ pexit("", 0);
+ /* not reached */
+ for(;;)
+ panic("tramp");
+ return 0;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ DWORD h;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+
+ p = newproc();
+ if(p == nil)
+ panic("out of kernel processes");
+ p->os = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if(p->os == NULL)
+ panic("can't allocate os event");
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->ui = up->env->ui;
+ kstrdup(&p->env->user, up->env->user);
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ p->pid = (int)CreateThread(0, 16384, tramp, p, 0, &h);
+ if(p->pid <= 0)
+ panic("ran out of kernel processes");
+}
+
+#if(_WIN32_WINNT >= 0x0400)
+void APIENTRY sleepintr(DWORD param)
+{
+}
+#endif
+
+void
+oshostintr(Proc *p)
+{
+ if (p->syscall == SOCK_SELECT)
+ return;
+ p->intwait = 0;
+#if(_WIN32_WINNT >= 0x0400)
+ if(p->syscall == SYS_SLEEP) {
+ QueueUserAPC(sleepintr, (HANDLE) p->pid, (DWORD) p->pid);
+ }
+#endif
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ longjmp(env, val);
+}
+
+int
+readkbd(void)
+{
+ DWORD r;
+ char buf[1];
+
+ if(ReadFile(kbdh, buf, sizeof(buf), &r, 0) == FALSE)
+ panic("keyboard fail");
+ if (r == 0)
+ panic("keyboard EOF");
+
+ if (buf[0] == 0x03) {
+ // INTR (CTRL+C)
+ termrestore();
+ ExitProcess(0);
+ }
+ if(buf[0] == '\r')
+ buf[0] = '\n';
+ return buf[0];
+}
+
+void
+cleanexit(int x)
+{
+ sleep(2); /* give user a chance to see message */
+ termrestore();
+ ExitProcess(x);
+}
+
+struct ecodes {
+ DWORD code;
+ char* name;
+} ecodes[] = {
+ EXCEPTION_ACCESS_VIOLATION, "segmentation violation",
+ EXCEPTION_DATATYPE_MISALIGNMENT, "data alignment",
+ EXCEPTION_BREAKPOINT, "breakpoint",
+ EXCEPTION_SINGLE_STEP, "single step",
+ EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "array bounds check",
+ EXCEPTION_FLT_DENORMAL_OPERAND, "denormalized float",
+ EXCEPTION_FLT_DIVIDE_BY_ZERO, "floating point divide by zero",
+ EXCEPTION_FLT_INEXACT_RESULT, "inexact floating point",
+ EXCEPTION_FLT_INVALID_OPERATION, "invalid floating operation",
+ EXCEPTION_FLT_OVERFLOW, "floating point result overflow",
+ EXCEPTION_FLT_STACK_CHECK, "floating point stack check",
+ EXCEPTION_FLT_UNDERFLOW, "floating point result underflow",
+ EXCEPTION_INT_DIVIDE_BY_ZERO, "divide by zero",
+ EXCEPTION_INT_OVERFLOW, "integer overflow",
+ EXCEPTION_PRIV_INSTRUCTION, "privileged instruction",
+ EXCEPTION_IN_PAGE_ERROR, "page-in error",
+ EXCEPTION_ILLEGAL_INSTRUCTION, "illegal instruction",
+ EXCEPTION_NONCONTINUABLE_EXCEPTION, "non-continuable exception",
+ EXCEPTION_STACK_OVERFLOW, "stack overflow",
+ EXCEPTION_INVALID_DISPOSITION, "invalid disposition",
+ EXCEPTION_GUARD_PAGE, "guard page violation",
+ 0, nil
+};
+
+LONG
+TrapHandler(LPEXCEPTION_POINTERS ureg)
+{
+ int i;
+ char *name;
+ DWORD code;
+ // WORD pc;
+ char buf[ERRMAX];
+
+ code = ureg->ExceptionRecord->ExceptionCode;
+ // pc = ureg->ContextRecord->Eip;
+
+ name = nil;
+ for(i = 0; i < nelem(ecodes); i++) {
+ if(ecodes[i].code == code) {
+ name = ecodes[i].name;
+ break;
+ }
+ }
+
+ if(name == nil) {
+ snprint(buf, sizeof(buf), "unknown trap type (%#.8lux)\n", code);
+ name = buf;
+ }
+/*
+ if(pc != 0) {
+ snprint(buf, sizeof(buf), "%s: pc=0x%lux", name, pc);
+ name = buf;
+ }
+*/
+ switch (code) {
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ case EXCEPTION_FLT_OVERFLOW:
+ case EXCEPTION_FLT_STACK_CHECK:
+ case EXCEPTION_FLT_UNDERFLOW:
+ /* clear exception flags and ensure safe empty state */
+ _asm { fnclex };
+ _asm { fninit };
+ }
+ disfault(nil, name);
+ /* not reached */
+ return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+static void
+termset(void)
+{
+ DWORD flag;
+
+ if(donetermset)
+ return;
+ donetermset = 1;
+ conh = GetStdHandle(STD_OUTPUT_HANDLE);
+ kbdh = GetStdHandle(STD_INPUT_HANDLE);
+ errh = GetStdHandle(STD_ERROR_HANDLE);
+ if(errh == INVALID_HANDLE_VALUE)
+ errh = conh;
+
+ // The following will fail if kbdh not from console (e.g. a pipe)
+ // in which case we don't care
+ GetConsoleMode(kbdh, &consolestate);
+ flag = consolestate;
+ flag = flag & ~(ENABLE_PROCESSED_INPUT|ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT);
+ SetConsoleMode(kbdh, flag);
+}
+
+void
+termrestore(void)
+{
+ if(kbdh != INVALID_HANDLE_VALUE)
+ SetConsoleMode(kbdh, consolestate);
+}
+
+static int rebootok = 0; /* is shutdown -r supported? */
+
+void
+osreboot(char *file, char **argv)
+{
+ if(rebootok){
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+ }
+}
+
+void
+libinit(char *imod)
+{
+ WSADATA wasdat;
+ DWORD lasterror, namelen;
+ OSVERSIONINFO os;
+ char sys[64], uname[64];
+ wchar_t wuname[64];
+ char *uns;
+
+ os.dwOSVersionInfoSize = sizeof(os);
+ if(!GetVersionEx(&os))
+ panic("can't get os version");
+ PlatformId = os.dwPlatformId;
+ if (PlatformId == VER_PLATFORM_WIN32_NT) { /* true for NT and 2000 */
+ rebootok = 1;
+ } else {
+ rebootok = 0;
+ }
+ termset();
+
+ if((int)INVALID_HANDLE_VALUE != -1 || sizeof(HANDLE) != sizeof(int))
+ panic("invalid handle value or size");
+
+ if(WSAStartup(MAKEWORD(1, 1), &wasdat) != 0)
+ panic("no winsock.dll");
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ if(sflag == 0)
+ SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)TrapHandler);
+
+ path = getenv("PATH");
+ if(path == nil)
+ path = ".";
+
+ up = newproc();
+ if(up == nil)
+ panic("cannot create kernel process");
+
+ strcpy(uname, "inferno");
+ namelen = sizeof(wuname);
+ if(GetUserName(wuname, &namelen) != TRUE) {
+ lasterror = GetLastError();
+ if(PlatformId == VER_PLATFORM_WIN32_NT || lasterror != ERROR_NOT_LOGGED_ON)
+ print("cannot GetUserName: %d\n", lasterror);
+ }else{
+ uns = narrowen(wuname);
+ snprint(uname, sizeof(uname), "%s", uns);
+ free(uns);
+ }
+ kstrdup(&eve, uname);
+
+ emuinit(imod);
+}
+
+void
+FPsave(void *fptr)
+{
+ _asm {
+ mov eax, fptr
+ fstenv [eax]
+ }
+}
+
+void
+FPrestore(void *fptr)
+{
+ _asm {
+ mov eax, fptr
+ fldenv [eax]
+ }
+}
+
+ulong
+umult(ulong a, ulong b, ulong *high)
+{
+ ulong lo, hi;
+
+ _asm {
+ mov eax, a
+ mov ecx, b
+ MUL ecx
+ mov lo, eax
+ mov hi, edx
+ }
+ *high = hi;
+ return lo;
+}
+
+int
+close(int fd)
+{
+ if(fd == -1)
+ return 0;
+ CloseHandle(ntfd2h(fd));
+ return 0;
+}
+
+int
+read(int fd, void *buf, uint n)
+{
+ HANDLE h;
+
+ if(fd == 0)
+ h = kbdh;
+ else
+ h = ntfd2h(fd);
+ if(h == INVALID_HANDLE_VALUE)
+ return -1;
+ if(!ReadFile(h, buf, n, &n, NULL))
+ return -1;
+ return n;
+}
+
+int
+write(int fd, void *buf, uint n)
+{
+ HANDLE h;
+
+ if(fd == 1 || fd == 2){
+ if(!donetermset)
+ termset();
+ if(fd == 1)
+ h = conh;
+ else
+ h = errh;
+ if(h == INVALID_HANDLE_VALUE)
+ return -1;
+ if(!WriteFile(h, buf, n, &n, NULL))
+ return -1;
+ return n;
+ }
+ if(!WriteFile(ntfd2h(fd), buf, n, &n, NULL))
+ return -1;
+ return n;
+}
+
+/*
+ * map handles and fds.
+ * this code assumes sizeof(HANDLE) == sizeof(int),
+ * that INVALID_HANDLE_VALUE is -1, and assumes
+ * that all tests of invalid fds check only for -1, not < 0
+ */
+int
+nth2fd(HANDLE h)
+{
+ return (int)h;
+}
+
+HANDLE
+ntfd2h(int fd)
+{
+ return (HANDLE)fd;
+}
+
+void
+oslopri(void)
+{
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
+}
+
+/* Resolve system header name conflict */
+#undef Sleep
+void
+sleep(int secs)
+{
+ Sleep(secs*1000);
+}
+
+void*
+sbrk(int size)
+{
+ void *brk;
+
+ brk = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ if(brk == 0)
+ return (void*)-1;
+
+ return brk;
+}
+
+ulong
+getcallerpc(void *arg)
+{
+ ulong cpc;
+ _asm {
+ mov eax, dword ptr [ebp]
+ mov eax, dword ptr [eax+4]
+ mov dword ptr cpc, eax
+ }
+ return cpc;
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ return GetTickCount();
+}
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ * days per month plus days/year
+ */
+static int dmsize[] =
+{
+ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static int ldmsize[] =
+{
+ 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * return the days/month for the given year
+ */
+static int*
+yrsize(int yr)
+{
+ /* a leap year is a multiple of 4, excluding centuries
+ * that are not multiples of 400 */
+ if( (yr % 4 == 0) && (yr % 100 != 0 || yr % 400 == 0) )
+ return ldmsize;
+ else
+ return dmsize;
+}
+
+static long
+tm2sec(SYSTEMTIME *tm)
+{
+ long secs;
+ int i, *d2m;
+
+ secs = 0;
+
+ /*
+ * seconds per year
+ */
+ for(i = 1970; i < tm->wYear; i++){
+ d2m = yrsize(i);
+ secs += d2m[0] * SEC2DAY;
+ }
+
+ /*
+ * seconds per month
+ */
+ d2m = yrsize(tm->wYear);
+ for(i = 1; i < tm->wMonth; i++)
+ secs += d2m[i] * SEC2DAY;
+
+ /*
+ * secs in last month
+ */
+ secs += (tm->wDay-1) * SEC2DAY;
+
+ /*
+ * hours, minutes, seconds
+ */
+ secs += tm->wHour * SEC2HOUR;
+ secs += tm->wMinute * SEC2MIN;
+ secs += tm->wSecond;
+
+ return secs;
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osusectime(void)
+{
+ SYSTEMTIME tm;
+ vlong secs;
+
+ GetSystemTime(&tm);
+ secs = tm2sec(&tm);
+ return secs * 1000000 + tm.wMilliseconds * 1000;
+}
+
+vlong
+osnsec(void)
+{
+ return osusectime()*1000; /* TO DO better */
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ SleepEx(milsec, FALSE);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ if (sleepers > MAXSLEEPERS)
+ return -1;
+ sleepers++;
+ up->syscall = SYS_SLEEP;
+ SleepEx(milsec, TRUE);
+ up->syscall = 0;
+ sleepers--;
+ return 0;
+}
+
+void
+osyield(void)
+{
+ SwitchToThread();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ sleep(1000000);
+}
+
+/*
+ * these should never be called, and are included
+ * as stubs since we are linking against a library which defines them
+ */
+int
+open(const char *path, int how, ...)
+{
+ panic("open");
+ return -1;
+}
+
+int
+creat(const char *path, int how)
+{
+ panic("creat");
+ return -1;
+}
+
+int
+stat(const char *path, struct stat *sp)
+{
+ panic("stat");
+ return -1;
+}
+
+int
+chown(const char *path, int uid, int gid)
+{
+ panic("chown");
+ return -1;
+}
+
+int
+chmod(const char *path, int mode)
+{
+ panic("chmod");
+ return -1;
+}
+
+void
+link(char *path, char *next)
+{
+ panic("link");
+}
+
+int
+segflush(void *a, ulong n)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/Nt/r16.c
@@ -1,0 +1,186 @@
+#define UNICODE
+#define Unknown win_Unknown
+#include <windows.h>
+#include <winbase.h>
+#undef Unknown
+#undef Sleep
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "r16.h"
+
+enum
+{
+ Bits10 = 0x03ff, /* 0011 1111 1111 */
+
+ R16self = 0x10000,
+
+ HSurrogateMin = 0xd800,
+ HSurrogateMax = 0xdbff,
+ LSurrogateMin = 0xdc00,
+ LSurrogateMax = 0xdfff,
+};
+
+Rune16*
+runes16dup(Rune16 *r)
+{
+ int n;
+ Rune16 *s;
+
+ n = runes16len(r) + 1;
+ s = malloc(n * sizeof(Rune16));
+ if(s == nil)
+ error(Enomem);
+ memmove(s, r, n * sizeof(Rune16));
+ return s;
+}
+
+int
+runes16len(Rune16 *r)
+{
+ int n;
+
+ n = 0;
+ while(*r++ != 0)
+ n++;
+ return n;
+}
+
+char*
+runes16toutf(char *p, Rune16 *r, int nc)
+{
+ char *op, *ep;
+ int n;
+ Rune c, lc;
+
+ op = p;
+ ep = p + nc;
+ while(c = *r++) {
+ if(c > Runemax)
+ c = Runeerror;
+ if(c >= LSurrogateMin && c <= LSurrogateMax)
+ c = Runeerror;
+ if(c >= HSurrogateMin && c<= HSurrogateMax){
+ lc = *r++;
+ if(lc >= LSurrogateMin || lc <= LSurrogateMax)
+ c = (c&Bits10)<<10 | (lc&Bits10) + R16self;
+ else
+ c = Runeerror;
+ }
+ n = runelen(c);
+ if(p + n >= ep)
+ break;
+ p += runetochar(p, &c);
+ }
+ *p = '\0';
+ return op;
+}
+
+int
+rune16nlen(Rune16 *r, int nrune)
+{
+ int nb;
+ Rune c;
+
+ nb = 0;
+ while(nrune--) {
+ c = *r++;
+ if(c < R16self)
+ nb += runelen(c);
+ else {
+ c -= R16self;
+ nb += runelen(HSurrogateMin | (c>>10));
+ nb += runelen(LSurrogateMin | (c&Bits10));
+ }
+ }
+ return nb;
+}
+
+Rune16*
+utftorunes16(Rune16 *r, char *p, int nc)
+{
+ Rune16 *or, *er;
+ Rune rc;
+
+ or = r;
+ er = r + nc;
+ while(*p != '\0' && r + 1 < er){
+ p += chartorune(&rc, p);
+ if(rc < R16self){
+ *r++ = rc;
+ continue;
+ }
+ if(rc > Runemax || er-r < 2){
+ *r++ = Runeerror;
+ continue;
+ }
+ rc -= R16self;
+ *r++ = HSurrogateMin | (rc>>10);
+ *r++ = LSurrogateMin | (rc&Bits10);
+ }
+ *r = '\0';
+ return or;
+}
+
+int
+runes16cmp(Rune16 *s1, Rune16 *s2)
+{
+ Rune16 r1, r2;
+
+ for(;;) {
+ r1 = *s1++;
+ r2 = *s2++;
+ if(r1 != r2) {
+ if(r1 > r2)
+ return 1;
+ return -1;
+ }
+ if(r1 == 0)
+ return 0;
+ }
+}
+
+wchar_t *
+widen(char *s)
+{
+ int n;
+ wchar_t *ws;
+
+ n = utflen(s) + 1;
+ ws = smalloc(n*sizeof(wchar_t));
+ utftorunes16(ws, s, n);
+ return ws;
+}
+
+
+char *
+narrowen(wchar_t *ws)
+{
+ char *s;
+ int n;
+
+ n = widebytes(ws);
+ s = smalloc(n);
+ runes16toutf(s, ws, n);
+ return s;
+}
+
+
+int
+widebytes(wchar_t *ws)
+{
+ int n = 0;
+ wchar_t c;
+
+ while (*ws){
+ c = *ws++;
+ if(c < R16self)
+ n += runelen(c);
+ else {
+ c -= R16self;
+ n += runelen(HSurrogateMin | (c>>10));
+ n += runelen(LSurrogateMin | (c&Bits10));
+ }
+ }
+ return n+1;
+}
--- /dev/null
+++ b/emu/Nt/r16.h
@@ -1,0 +1,11 @@
+typedef unsigned short Rune16;
+
+wchar_t *widen(char *s);
+char *narrowen(wchar_t *ws);
+int widebytes(wchar_t *ws);
+int runes16len(Rune16*);
+int rune16nlen(Rune16*, int);
+Rune16* runes16dup(Rune16*);
+Rune16* utftorunes16(Rune16*, char*, int);
+char* runes16toutf(char*, Rune16*, int);
+int runes16cmp(Rune16*, Rune16*);
--- /dev/null
+++ b/emu/Nt/vlrt.c
@@ -1,0 +1,751 @@
+#include "dat.h"
+
+/*
+ * typedef unsigned long ulong;
+ * typedef unsigned int uint;
+ * typedef unsigned short ushort;
+ * typedef unsigned char uchar;
+ * typedef signed char schar;
+*/
+
+#define SIGN(n) (1UL<<(n-1))
+
+typedef struct Vlong Vlong;
+struct Vlong
+{
+ ulong lo;
+ ulong hi;
+};
+
+void abort(void);
+
+void
+_addv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo + b.lo;
+ hi = a.hi + b.hi;
+ if(lo < a.lo)
+ hi++;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+void
+_subv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo - b.lo;
+ hi = a.hi - b.hi;
+ if(lo > a.lo)
+ hi--;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+void
+_mulv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong ahi, alo, chi, clo, x;
+ int i;
+
+ ahi = a.hi;
+ alo = a.lo;
+ chi = 0;
+ clo = 0;
+
+ x = b.lo;
+ for(i=0; i<32; i++) {
+ if(x & 1) {
+ chi += ahi;
+ clo += alo;
+ if(clo < alo)
+ chi++;
+ }
+ ahi <<= 1;
+ if(alo & SIGN(32))
+ ahi += 1;
+ alo <<= 1;
+ x >>= 1;
+ }
+
+ /*
+ * same, but
+ * alo is known to be 0
+ * can stop when x == 0
+ */
+ for(x=b.hi; x; x>>=1) {
+ if(x & 1)
+ chi += ahi;
+ ahi <<= 1;
+ }
+
+ r->hi = chi;
+ r->lo = clo;
+}
+
+void
+_d2v(Vlong *y, double d)
+{
+ Vlong x;
+ ulong xhi, xlo, ylo, yhi;
+ int sh;
+
+ *(double*)&x = d;
+
+ xhi = (x.hi & 0xfffff) | 0x100000;
+ xlo = x.lo;
+ sh = 1075 - ((x.hi >> 20) & 0x7ff);
+
+ ylo = 0;
+ yhi = 0;
+ if(sh >= 0) {
+ /* v = (hi||lo) >> sh */
+ if(sh < 32) {
+ if(sh == 0) {
+ ylo = xlo;
+ yhi = xhi;
+ } else {
+ ylo = (xlo >> sh) | (xhi << (32-sh));
+ yhi = xhi >> sh;
+ }
+ } else {
+ if(sh == 32) {
+ ylo = xhi;
+ } else
+ if(sh < 64) {
+ ylo = xhi >> (sh-32);
+ }
+ }
+ } else {
+ /* v = (hi||lo) << -sh */
+ sh = -sh;
+ if(sh <= 10) {
+ ylo = xlo << sh;
+ yhi = (xhi << sh) | (xlo >> (32-sh));
+ } else {
+ /* overflow */
+ yhi = d; /* causes something awful */
+ }
+ }
+ if(x.hi & SIGN(32)) {
+ if(ylo != 0) {
+ ylo = -ylo;
+ yhi = ~yhi;
+ } else
+ yhi = -yhi;
+ }
+
+ y->hi = yhi;
+ y->lo = ylo;
+}
+
+void
+_f2v(Vlong *y, float f)
+{
+
+ _d2v(y, f);
+}
+
+double
+_v2d(Vlong x)
+{
+ if(x.hi & SIGN(32)) {
+ if(x.lo) {
+ x.lo = -x.lo;
+ x.hi = ~x.hi;
+ } else
+ x.hi = -x.hi;
+ return -((long)x.hi*4294967296. + x.lo);
+ }
+ return (long)x.hi*4294967296. + x.lo;
+}
+
+float
+_v2f(Vlong x)
+{
+ return _v2d(x);
+}
+
+static void
+dodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
+{
+ ulong numlo, numhi, denhi, denlo, quohi, quolo, t;
+ int i;
+
+ numhi = num.hi;
+ numlo = num.lo;
+ denhi = den.hi;
+ denlo = den.lo;
+
+ /*
+ * get a divide by zero
+ */
+ if(denlo==0 && denhi==0) {
+ numlo = numlo / denlo;
+ }
+
+ /*
+ * set up the divisor and find the number of iterations needed
+ */
+ if(numhi >= SIGN(32)) {
+ quohi = SIGN(32);
+ quolo = 0;
+ } else {
+ quohi = numhi;
+ quolo = numlo;
+ }
+ i = 0;
+ while(denhi < quohi || (denhi == quohi && denlo < quolo)) {
+ denhi = (denhi<<1) | (denlo>>31);
+ denlo <<= 1;
+ i++;
+ }
+
+ quohi = 0;
+ quolo = 0;
+ for(; i >= 0; i--) {
+ quohi = (quohi<<1) | (quolo>>31);
+ quolo <<= 1;
+ if(numhi > denhi || (numhi == denhi && numlo >= denlo)) {
+ t = numlo;
+ numlo -= denlo;
+ if(numlo > t)
+ numhi--;
+ numhi -= denhi;
+ quolo |= 1;
+ }
+ denlo = (denlo>>1) | (denhi<<31);
+ denhi >>= 1;
+ }
+
+ if(q) {
+ q->lo = quolo;
+ q->hi = quohi;
+ }
+ if(r) {
+ r->lo = numlo;
+ r->hi = numhi;
+ }
+}
+
+void
+_divvu(Vlong *q, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ q->hi = 0;
+ q->lo = n.lo / d.lo;
+ return;
+ }
+ dodiv(n, d, q, 0);
+}
+
+void
+_modvu(Vlong *r, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ r->hi = 0;
+ r->lo = n.lo % d.lo;
+ return;
+ }
+ dodiv(n, d, 0, r);
+}
+
+static void
+vneg(Vlong *v)
+{
+
+ if(v->lo == 0) {
+ v->hi = -v->hi;
+ return;
+ }
+ v->lo = -v->lo;
+ v->hi = ~v->hi;
+}
+
+void
+_divv(Vlong *q, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ q->lo = (long)n.lo / (long)d.lo;
+ q->hi = ((long)q->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, q, 0);
+ if(nneg != dneg)
+ vneg(q);
+}
+
+void
+_modv(Vlong *r, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ r->lo = (long)n.lo % (long)d.lo;
+ r->hi = ((long)r->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, 0, r);
+ if(nneg)
+ vneg(r);
+}
+
+void
+_rshav(Vlong *r, Vlong a, int b)
+{
+ long t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = t>>31;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = t>>31;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_rshlv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = 0;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_lshv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.lo;
+ if(b >= 32) {
+ r->lo = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->hi = 0;
+ return;
+ }
+ r->hi = t << (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->lo = t;
+ r->hi = a.hi;
+ return;
+ }
+ r->lo = t << b;
+ r->hi = (t >> (32-b)) | (a.hi << b);
+}
+
+void
+_andv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi & b.hi;
+ r->lo = a.lo & b.lo;
+}
+
+void
+_orv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi | b.hi;
+ r->lo = a.lo | b.lo;
+}
+
+void
+_xorv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi ^ b.hi;
+ r->lo = a.lo ^ b.lo;
+}
+
+void
+_vpp(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+}
+
+void
+_vmm(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+}
+
+void
+_ppv(Vlong *l, Vlong *r)
+{
+
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_mmv(Vlong *l, Vlong *r)
+{
+
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
+{
+ Vlong t, u;
+
+ u.lo = 0;
+ u.hi = 0;
+ switch(type) {
+ default:
+ abort();
+ break;
+
+ case 1: /* schar */
+ t.lo = *(schar*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(schar*)lv = u.lo;
+ break;
+
+ case 2: /* uchar */
+ t.lo = *(uchar*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uchar*)lv = u.lo;
+ break;
+
+ case 3: /* short */
+ t.lo = *(short*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(short*)lv = u.lo;
+ break;
+
+ case 4: /* ushort */
+ t.lo = *(ushort*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ushort*)lv = u.lo;
+ break;
+
+ case 9: /* int */
+ t.lo = *(int*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(int*)lv = u.lo;
+ break;
+
+ case 10: /* uint */
+ t.lo = *(uint*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uint*)lv = u.lo;
+ break;
+
+ case 5: /* long */
+ t.lo = *(long*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(long*)lv = u.lo;
+ break;
+
+ case 6: /* ulong */
+ t.lo = *(ulong*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ulong*)lv = u.lo;
+ break;
+
+ case 7: /* vlong */
+ case 8: /* uvlong */
+ fn(&u, *(Vlong*)lv, rv);
+ *(Vlong*)lv = u;
+ break;
+ }
+ *ret = u;
+}
+
+void
+_p2v(Vlong *ret, void *p)
+{
+ long t;
+
+ t = (ulong)p;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sl2v(Vlong *ret, long sl)
+{
+ long t;
+
+ t = sl;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ul2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_si2v(Vlong *ret, int si)
+{
+ long t;
+
+ t = si;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ui2v(Vlong *ret, uint ui)
+{
+ long t;
+
+ t = ui;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sh2v(Vlong *ret, long sh)
+{
+ long t;
+
+ t = (sh << 16) >> 16;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uh2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xffff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sc2v(Vlong *ret, long uc)
+{
+ long t;
+
+ t = (uc << 24) >> 24;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uc2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+long
+_v2sc(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xff;
+ return (t << 24) >> 24;
+}
+
+long
+_v2uc(Vlong rv)
+{
+
+ return rv.lo & 0xff;
+}
+
+long
+_v2sh(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xffff;
+ return (t << 16) >> 16;
+}
+
+long
+_v2uh(Vlong rv)
+{
+
+ return rv.lo & 0xffff;
+}
+
+long
+_v2sl(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ul(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2si(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ui(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+int
+_testv(Vlong rv)
+{
+ return rv.lo || rv.hi;
+}
+
+int
+_eqv(Vlong lv, Vlong rv)
+{
+ return lv.lo == rv.lo && lv.hi == rv.hi;
+}
+
+int
+_nev(Vlong lv, Vlong rv)
+{
+ return lv.lo != rv.lo || lv.hi != rv.hi;
+}
+
+int
+_ltv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_gtv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_gev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
+
+int
+_lov(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lsv(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_hiv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_hsv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
--- /dev/null
+++ b/emu/Nt/win.c
@@ -1,0 +1,798 @@
+#define Unknown WUnknown
+#define Colormap WColormap
+#define Cursor WCursor
+#define Display WDisplay
+#define Drawable WDrawable
+#define Font WFont
+#define GC WGC
+#define Point WPoint
+#define Rectangle WRectangle
+#define Screen WScreen
+#define Visual WVisual
+#define Window WWindow
+
+#include <windows.h>
+
+#undef Colormap
+#undef Cursor
+#undef Display
+#undef XDrawable
+#undef Font
+#undef GC
+#undef Point
+#undef Rectangle
+#undef Screen
+#undef Visual
+#undef Window
+#undef Unknown
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <draw.h>
+#include "keyboard.h"
+#include "cursor.h"
+#include "r16.h"
+
+extern ulong displaychan;
+
+extern int bytesperline(Rectangle, int);
+extern int main(int argc, char **argv);
+static void dprint(char*, ...);
+static DWORD WINAPI winproc(LPVOID);
+
+static HINSTANCE inst;
+static HINSTANCE previnst;
+static int cmdshow;
+static HWND window;
+static HDC screen;
+static HPALETTE palette;
+static int maxxsize;
+static int maxysize;
+static int attached;
+static int isunicode = 1;
+static HCURSOR hcursor;
+
+char *argv0 = "inferno";
+static ulong *data;
+
+extern DWORD PlatformId;
+char* gkscanid = "emu_win32vk";
+
+int WINAPI
+WinMain(HINSTANCE winst, HINSTANCE wprevinst, LPSTR cmdline, int wcmdshow)
+{
+ inst = winst;
+ previnst = wprevinst;
+ cmdshow = wcmdshow;
+
+ /* cmdline passed into WinMain does not contain name of executable.
+ * The globals __argc and __argv to include this info - like UNIX
+ */
+ main(__argc, __argv);
+ return 0;
+}
+
+static void
+dprint(char *fmt, ...)
+{
+ va_list arg;
+ char buf[128];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, (LPSTR)arg);
+ va_end(arg);
+ OutputDebugString("inferno: ");
+ OutputDebugString(buf);
+}
+
+static void
+graphicscmap(PALETTEENTRY *pal)
+{
+ int r, g, b, cr, cg, cb, v, p;
+ int num, den;
+ int i, j;
+ for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){
+ for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){
+ den=r;
+ if(g>den) den=g;
+ if(b>den) den=b;
+ if(den==0) /* divide check -- pick grey shades */
+ cr=cg=cb=v*17;
+ else{
+ num=17*(4*den+v);
+ cr=r*num/den;
+ cg=g*num/den;
+ cb=b*num/den;
+ }
+ p = i+(j&15);
+ pal[p].peRed = cr*0x01010101;
+ pal[p].peGreen = cg*0x01010101;
+ pal[p].peBlue = cb*0x01010101;
+ pal[p].peFlags = 0;
+ }
+ }
+}
+
+static void
+graphicsgmap(PALETTEENTRY *pal, int d)
+{
+ int i, j, s, m, p;
+
+ s = 8-d;
+ m = 1;
+ while(--d >= 0)
+ m *= 2;
+ m = 255/(m-1);
+ for(i=0; i < 256; i++){
+ j = (i>>s)*m;
+ p = 255-i;
+ pal[p].peRed = pal[p].peGreen = pal[p].peBlue = (255-j)*0x01010101;
+ pal[p].peFlags = 0;
+ }
+}
+
+static ulong
+autochan(void)
+{
+ HDC dc;
+ int bpp;
+
+ dc = GetDC(NULL);
+ if (dc == NULL)
+ return CMAP8;
+
+ bpp = GetDeviceCaps(dc, BITSPIXEL);
+ if (bpp < 15)
+ return CMAP8;
+ if (bpp < 24)
+ return RGB15;
+ if (bpp < 32)
+ return RGB24;
+ return XRGB32;
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+ int i, k;
+ ulong c;
+ DWORD h;
+ RECT bs;
+ RGBQUAD *rgb;
+ HBITMAP bits;
+ BITMAPINFO *bmi;
+ LOGPALETTE *logpal;
+ PALETTEENTRY *pal;
+ int bsh, bsw, sx, sy;
+
+ if(attached)
+ goto Return;
+
+ /* Compute bodersizes */
+ memset(&bs, 0, sizeof(bs));
+ AdjustWindowRect(&bs, WS_OVERLAPPEDWINDOW, 0);
+ bsw = bs.right - bs.left;
+ bsh = bs.bottom - bs.top;
+ sx = GetSystemMetrics(SM_CXFULLSCREEN) - bsw;
+ Xsize -= Xsize % 4; /* Round down */
+ if(Xsize > sx)
+ Xsize = sx;
+ sy = GetSystemMetrics(SM_CYFULLSCREEN) - bsh + 20;
+ if(Ysize > sy)
+ Ysize = sy;
+
+ logpal = malloc(sizeof(LOGPALETTE) + 256*sizeof(PALETTEENTRY));
+ if(logpal == nil)
+ return nil;
+ logpal->palVersion = 0x300;
+ logpal->palNumEntries = 256;
+ pal = logpal->palPalEntry;
+
+ c = displaychan;
+ if(c == 0)
+ c = autochan();
+ k = 8;
+ if(TYPE(c) == CGrey){
+ graphicsgmap(pal, NBITS(c));
+ c = GREY8;
+ }else{
+ if(c == RGB15)
+ k = 16;
+ else if(c == RGB24)
+ k = 24;
+ else if(c == XRGB32)
+ k = 32;
+ else
+ c = CMAP8;
+ graphicscmap(pal);
+ }
+
+ palette = CreatePalette(logpal);
+
+ if(k == 8)
+ bmi = malloc(sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD));
+ else
+ bmi = malloc(sizeof(BITMAPINFOHEADER));
+ if(bmi == nil)
+ return nil;
+ bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi->bmiHeader.biWidth = Xsize;
+ bmi->bmiHeader.biHeight = -Ysize; /* - => origin upper left */
+ bmi->bmiHeader.biPlanes = 1; /* always 1 */
+ bmi->bmiHeader.biBitCount = k;
+ bmi->bmiHeader.biCompression = BI_RGB;
+ bmi->bmiHeader.biSizeImage = 0; /* Xsize*Ysize*(k/8) */
+ bmi->bmiHeader.biXPelsPerMeter = 0;
+ bmi->bmiHeader.biYPelsPerMeter = 0;
+ bmi->bmiHeader.biClrUsed = 0;
+ bmi->bmiHeader.biClrImportant = 0; /* number of important colors: 0 means all */
+
+ if(k == 8){
+ rgb = bmi->bmiColors;
+ for(i = 0; i < 256; i++){
+ rgb[i].rgbRed = pal[i].peRed;
+ rgb[i].rgbGreen = pal[i].peGreen;
+ rgb[i].rgbBlue = pal[i].peBlue;
+ }
+ }
+
+ screen = CreateCompatibleDC(NULL);
+ if(screen == nil){
+ fprint(2, "screen dc nil\n");
+ return nil;
+ }
+
+ if(SelectPalette(screen, palette, 1) == nil){
+ fprint(2, "select pallete failed\n");
+ }
+ i = RealizePalette(screen);
+ GdiFlush();
+ bits = CreateDIBSection(screen, bmi, DIB_RGB_COLORS, &data, nil, 0);
+ if(bits == nil){
+ fprint(2, "CreateDIBSection failed\n");
+ return nil;
+ }
+
+ SelectObject(screen, bits);
+ GdiFlush();
+ CreateThread(0, 16384, winproc, nil, 0, &h);
+ attached = 1;
+
+ Return:
+ r->min.x = 0;
+ r->min.y = 0;
+ r->max.x = Xsize;
+ r->max.y = Ysize;
+ displaychan = c;
+ *chan = c;
+ *d = k;
+ *width = (Xsize/4)*(k/8);
+ *softscreen = 1;
+ return (uchar*)data;
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ RECT wr;
+
+ if(r.max.x<=r.min.x || r.max.y<=r.min.y)
+ return;
+ wr.left = r.min.x;
+ wr.top = r.min.y;
+ wr.right = r.max.x;
+ wr.bottom = r.max.y;
+ InvalidateRect(window, &wr, 0);
+}
+
+static void
+scancode(WPARAM wparam, LPARAM lparam, int keyup)
+{
+ uchar buf[2];
+
+ if(!(lparam & (1<<30))) { /* don't auto-repeat chars */
+ buf[0] = wparam;
+ buf[1] = wparam >> 8;
+ if (keyup)
+ buf[1] |= 0x80;
+ qproduce(gkscanq, buf, sizeof buf);
+ }
+}
+
+LRESULT CALLBACK
+WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ PAINTSTRUCT paint;
+ HDC hdc;
+ LPMINMAXINFO mmi;
+ LONG x, y, w, h, b;
+ HCURSOR dcurs;
+ POINT m;
+
+ switch(msg) {
+ case WM_SETCURSOR:
+ /* User set */
+ if(hcursor != NULL) {
+ SetCursor(hcursor);
+ break;
+ }
+ /* Pick the default */
+ dcurs = LoadCursor(NULL, IDC_ARROW);
+ SetCursor(dcurs);
+ break;
+ case WM_MOUSEWHEEL:
+ if((int)wparam>0)
+ b = 8;
+ else
+ b = 16;
+ m.x = LOWORD(lparam);
+ m.y = HIWORD(lparam);
+ ScreenToClient(hwnd, &m);
+ goto mok;
+ case WM_LBUTTONDBLCLK:
+ b = (1<<8) | 1;
+ goto process;
+ case WM_MBUTTONDBLCLK:
+ b = (1<<8) | 2;
+ goto process;
+ case WM_RBUTTONDBLCLK:
+ b = (1<<8) | 4;
+ goto process;
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ b = 0;
+ process:
+ m.x = LOWORD(lparam);
+ m.y = HIWORD(lparam);
+ mok:
+ if(wparam & MK_LBUTTON)
+ b |= 1;
+ if(wparam & MK_MBUTTON)
+ b |= 2;
+ if(wparam & MK_RBUTTON) {
+ if(wparam & MK_CONTROL)
+ b |= 2; //simulate middle button
+ else
+ b |= 4; //right button
+ }
+ mousetrack(b, m.x, m.y, 0);
+ break;
+ case WM_SYSKEYDOWN:
+ if(gkscanq)
+ scancode(wparam, lparam, 0);
+ break;
+ case WM_SYSKEYUP:
+ if(gkscanq)
+ scancode(wparam, lparam, 1);
+ else if(wparam == VK_MENU)
+ gkbdputc(gkbdq, Latin);
+ break;
+ case WM_KEYDOWN:
+ if(gkscanq) {
+ scancode(wparam, lparam, 0);
+ break;
+ }
+ switch(wparam) {
+ default:
+ return 0;
+ case VK_HOME:
+ wparam = Home;
+ break;
+ case VK_END:
+ wparam = End;
+ break;
+ case VK_UP:
+ wparam = Up;
+ break;
+ case VK_DOWN:
+ wparam = Down;
+ break;
+ case VK_LEFT:
+ wparam = Left;
+ break;
+ case VK_RIGHT:
+ wparam = Right;
+ break;
+ case VK_PRIOR: /* VK_PAGE_UP */
+ wparam = Pgup;
+ break;
+ case VK_NEXT: /* VK_PAGE_DOWN */
+ wparam = Pgdown;
+ break;
+ case VK_PRINT:
+ wparam = Print;
+ break;
+ case VK_SCROLL:
+ wparam = Scroll;
+ break;
+ case VK_PAUSE:
+ wparam = Pause;
+ break;
+ case VK_INSERT:
+ wparam = Ins;
+ break;
+ case VK_DELETE:
+ wparam = Del;
+ break;
+/*
+ case VK_TAB:
+ if(GetKeyState(VK_SHIFT)<0)
+ wparam = BackTab;
+ else
+ wparam = '\t';
+ break;
+*/
+ }
+ gkbdputc(gkbdq, wparam);
+ break;
+ case WM_KEYUP:
+ if(gkscanq)
+ scancode(wparam, lparam, 1);
+ break;
+ case WM_CHAR:
+ if(gkscanq)
+ break;
+ switch(wparam) {
+ case '\n':
+ wparam = '\r';
+ break;
+ case '\r':
+ wparam = '\n';
+ break;
+ case '\t':
+ if(GetKeyState(VK_SHIFT)<0)
+ wparam = BackTab;
+ else
+ wparam = '\t';
+ break;
+ }
+ if(lparam & KF_ALTDOWN)
+ wparam = APP | (wparam & 0xFF);
+ gkbdputc(gkbdq, wparam);
+ break;
+ case WM_CLOSE:
+ DestroyWindow(hwnd);
+ break;
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ cleanexit(0);
+ break;
+ case WM_PALETTECHANGED:
+ if((HWND)wparam == hwnd)
+ break;
+ /* fall through */
+ case WM_QUERYNEWPALETTE:
+ hdc = GetDC(hwnd);
+ SelectPalette(hdc, palette, 0);
+ if(RealizePalette(hdc) != 0)
+ InvalidateRect(hwnd, nil, 0);
+ ReleaseDC(hwnd, hdc);
+ break;
+ case WM_PAINT:
+ hdc = BeginPaint(hwnd, &paint);
+ SelectPalette(hdc, palette, 0);
+ RealizePalette(hdc);
+ x = paint.rcPaint.left;
+ y = paint.rcPaint.top;
+ w = paint.rcPaint.right - x;
+ h = paint.rcPaint.bottom - y;
+ BitBlt(hdc, x, y, w, h, screen, x, y, SRCCOPY);
+ EndPaint(hwnd, &paint);
+ break;
+ case WM_GETMINMAXINFO:
+ mmi = (LPMINMAXINFO)lparam;
+ mmi->ptMaxSize.x = maxxsize;
+ mmi->ptMaxSize.y = maxysize;
+ mmi->ptMaxTrackSize.x = maxxsize;
+ mmi->ptMaxTrackSize.y = maxysize;
+ break;
+ case WM_SYSCHAR:
+ case WM_COMMAND:
+ case WM_CREATE:
+ case WM_SETFOCUS:
+ case WM_DEVMODECHANGE:
+ case WM_WININICHANGE:
+ case WM_INITMENU:
+ default:
+ if(isunicode)
+ return DefWindowProcW(hwnd, msg, wparam, lparam);
+ return DefWindowProcA(hwnd, msg, wparam, lparam);
+ }
+ return 0;
+}
+
+static DWORD WINAPI
+winproc(LPVOID x)
+{
+ MSG msg;
+ RECT size;
+ WNDCLASSW wc;
+ WNDCLASSA wca;
+ DWORD ws;
+
+ if(!previnst){
+ wc.style = CS_DBLCLKS;
+ wc.lpfnWndProc = WindowProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = inst;
+ wc.hIcon = LoadIcon(inst, MAKEINTRESOURCE(100));
+ wc.hCursor = NULL;
+ wc.hbrBackground = GetStockObject(WHITE_BRUSH);
+
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = L"inferno";
+
+ if(RegisterClassW(&wc) == 0){
+ wca.style = wc.style;
+ wca.lpfnWndProc = wc.lpfnWndProc;
+ wca.cbClsExtra = wc.cbClsExtra;
+ wca.cbWndExtra = wc.cbWndExtra;
+ wca.hInstance = wc.hInstance;
+ wca.hIcon = wc.hIcon;
+ wca.hCursor = wc.hCursor;
+ wca.hbrBackground = wc.hbrBackground;
+
+ wca.lpszMenuName = 0;
+ wca.lpszClassName = "inferno";
+ isunicode = 0;
+
+ RegisterClassA(&wca);
+ }
+ }
+
+ size.left = 0;
+ size.top = 0;
+ size.right = Xsize;
+ size.bottom = Ysize;
+
+ ws = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX;
+
+ if(AdjustWindowRect(&size, ws, 0)) {
+ maxxsize = size.right - size.left;
+ maxysize = size.bottom - size.top;
+ }else{
+ maxxsize = Xsize + 40;
+ maxysize = Ysize + 40;
+ }
+
+ if(isunicode) {
+ window = CreateWindowExW(
+ 0, /* extended style */
+ L"inferno", /* class */
+ L"Inferno", /* caption */
+ ws, /* style */
+ CW_USEDEFAULT, /* init. x pos */
+ CW_USEDEFAULT, /* init. y pos */
+ maxxsize, /* init. x size */
+ maxysize, /* init. y size */
+ NULL, /* parent window (actually owner window for overlapped) */
+ NULL, /* menu handle */
+ inst, /* program handle */
+ NULL /* create parms */
+ );
+ }else{
+ window = CreateWindowExA(
+ 0, /* extended style */
+ "inferno", /* class */
+ "Inferno", /* caption */
+ ws, /* style */
+ CW_USEDEFAULT, /* init. x pos */
+ CW_USEDEFAULT, /* init. y pos */
+ maxxsize, /* init. x size */
+ maxysize, /* init. y size */
+ NULL, /* parent window (actually owner window for overlapped) */
+ NULL, /* menu handle */
+ inst, /* program handle */
+ NULL /* create parms */
+ );
+ }
+
+ if(window == nil){
+ fprint(2, "can't make window\n");
+ ExitThread(0);
+ }
+
+ SetForegroundWindow(window);
+ ShowWindow(window, cmdshow);
+ UpdateWindow(window);
+ // CloseWindow(window);
+
+ if(isunicode) {
+ while(GetMessageW(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ }else{
+ while(GetMessageA(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessageA(&msg);
+ }
+ }
+ attached = 0;
+ ExitThread(msg.wParam);
+ return 0;
+}
+
+void
+setpointer(int x, int y)
+{
+ POINT pt;
+
+ pt.x = x; pt.y = y;
+ ClientToScreen(window, &pt);
+ SetCursorPos(pt.x, pt.y);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+ HCURSOR nh, oh;
+ Rectangle ir;
+ int i, h, j, bpl, ch, cw;
+ uchar *bs, *bc, *and, *xor, *cand, *cxor;
+
+ /* Set the default system cursor */
+ if(c->data == nil) {
+ oh = hcursor;
+ hcursor = NULL;
+ if(oh != NULL) {
+ SendMessage(window, WM_SETCURSOR, (int)window, 0);
+ DestroyCursor(oh);
+ }
+ return;
+ }
+
+ ir.min.x = c->minx;
+ ir.min.y = c->miny;
+ ir.max.x = c->maxx;
+ ir.max.y = c->maxy;
+ bpl = bytesperline(ir, 1);
+
+ h = (c->maxy-c->miny)/2;
+
+ ch = GetSystemMetrics(SM_CYCURSOR);
+ cw = (GetSystemMetrics(SM_CXCURSOR)+7)/8;
+
+ i = ch*cw;
+ and = malloc(2*i);
+ if(and == nil)
+ return;
+ xor = and + i;
+ memset(and, 0xff, i);
+ memset(xor, 0, i);
+
+ cand = and;
+ cxor = xor;
+ bc = c->data;
+ bs = c->data + h*bpl;
+
+ for(i = 0; i < ch && i < h; i++) {
+ for(j = 0; j < cw && j < bpl; j++) {
+ cand[j] = ~(bs[j] | bc[j]);
+ cxor[j] = ~bs[j] & bc[j];
+ }
+ cand += cw;
+ cxor += cw;
+ bs += bpl;
+ bc += bpl;
+ }
+ nh = CreateCursor(inst, -c->hotx, -c->hoty, 8*cw, ch, and, xor);
+ if(nh != NULL) {
+ oh = hcursor;
+ hcursor = nh;
+ SendMessage(window, WM_SETCURSOR, (int)window, 0);
+ if(oh != NULL)
+ DestroyCursor(oh);
+ }else{
+ print("CreateCursor error %d\n", GetLastError());
+ print("CXCURSOR=%d\n", GetSystemMetrics(SM_CXCURSOR));
+ print("CYCURSOR=%d\n", GetSystemMetrics(SM_CYCURSOR));
+ }
+ free(and);
+}
+
+/*
+ * thanks to drawterm for these
+ */
+
+static char*
+clipreadunicode(HANDLE h)
+{
+ Rune16 *p;
+ int n;
+ char *q;
+
+ p = GlobalLock(h);
+ n = rune16nlen(p, runes16len(p)+1);
+ q = malloc(n);
+ if(q != nil)
+ runes16toutf(q, p, n);
+ GlobalUnlock(h);
+
+ if(q == nil)
+ error(Enovmem);
+ return q;
+}
+
+static char *
+clipreadutf(HANDLE h)
+{
+ uchar *p;
+
+ p = GlobalLock(h);
+ p = strdup(p);
+ GlobalUnlock(h);
+
+ if(p == nil)
+ error(Enovmem);
+ return p;
+}
+
+char*
+clipread(void)
+{
+ HANDLE h;
+ char *p;
+
+ if(!OpenClipboard(window))
+ return strdup("");
+
+ if((h = GetClipboardData(CF_UNICODETEXT)))
+ p = clipreadunicode(h);
+ else if((h = GetClipboardData(CF_TEXT)))
+ p = clipreadutf(h);
+ else
+ p = strdup("");
+
+ CloseClipboard();
+ return p;
+}
+
+int
+clipwrite(char *buf)
+{
+ HANDLE h;
+ char *p;
+ Rune16 *rp;
+ int n;
+
+ n = 0;
+ if(buf != nil)
+ n = strlen(buf);
+ if(!OpenClipboard(window))
+ return -1;
+
+ if(!EmptyClipboard()){
+ CloseClipboard();
+ return -1;
+ }
+
+ h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, (n+1)*sizeof(Rune16));
+ if(h == NULL)
+ error(Enovmem);
+ rp = GlobalLock(h);
+ utftorunes16(rp, buf, n+1);
+ GlobalUnlock(h);
+
+ SetClipboardData(CF_UNICODETEXT, h);
+
+ h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, n+1);
+ if(h == NULL)
+ error(Enovmem);
+ p = GlobalLock(h);
+ memmove(p, buf, n);
+ p[n] = 0;
+ GlobalUnlock(h);
+
+ SetClipboardData(CF_TEXT, h);
+
+ CloseClipboard();
+ return n;
+}
--- /dev/null
+++ b/emu/OpenBSD/asm-386.S
@@ -1,0 +1,111 @@
+ .file "asm-OpenBSD-386.S"
+
+#include <sys/syscall.h>
+#include <machine/asm.h>
+
+#include "rfork_thread.S"
+
+/*
+ * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg)
+ */
+
+ .type ournewstack,@function
+ .global executeonnewstack
+executeonnewstack:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+
+ movl 8(%ebp), %esi /* get tos */
+ subl $4, %esi
+ movl 16(%ebp), %eax
+ movl %eax, (%esi) /* stash arg on new stack */
+ subl $4, %esi
+ movl 12(%ebp), %eax
+ movl %eax, (%esi) /* stash tramp on new stack */
+ mov %esi, %esp /* swap stacks pronto */
+ popl %eax /* recover the tramp address */
+ call *%eax /* and jump to it (ho ho) */
+
+ /* if we return here, tramp didn't do it's job */
+
+ addl $8, %esp /* clean up for pose value */
+
+ leal SYS_exit, %eax
+ int $0x80
+
+/*
+ * unlockandexit(int *key)
+ *
+ * NB: the return status may be rubbish if the stack is reused
+ * between the unlock and the system call, but this should
+ * not matter since no task is waiting for the result
+ */
+
+ .type unlockandexit,@function
+ .global unlockandexit
+unlockandexit:
+ pushl %ebp
+ movl %esp, %ebp
+
+ movl 8(%ebp), %esi /* get the key address */
+ pushl $0 /* exit status 0 */
+ movl $0, %eax /* unlock the stack allocator */
+ movl %eax, (%esi)
+ leal SYS_exit, %eax /* call exit */
+ int $0x80
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .type umult,@function
+ .global umult
+umult:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+
+ movl 8(%ebp), %eax
+ movl 12(%ebp), %ebx
+ mull %ebx
+ movl 16(%ebp), %ebx
+ movl %edx, (%ebx)
+
+ popl %ebx
+ popl %ebp
+ ret
+
+ .type FPsave,@function
+ .global FPsave
+FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .type FPrestore,@function
+ .global FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .type getcallerpc,@function
+ .global getcallerpc
+getcallerpc:
+ movl 4(%ebp), %eax
+ ret
+
+ .type _tas,@function
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/OpenBSD/audio.c
@@ -1,0 +1,547 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <soundcard.h>
+
+#define Audio_Mic_Val SOUND_MIXER_MIC
+#define Audio_Linein_Val SOUND_MIXER_LINE
+
+#define Audio_Speaker_Val SOUND_MIXER_SPEAKER
+#define Audio_Headphone_Val SOUND_MIXER_PHONEOUT
+#define Audio_Lineout_Val SOUND_MIXER_VOLUME
+
+#define Audio_Pcm_Val AFMT_S16_LE
+#define Audio_Ulaw_Val AFMT_MU_LAW
+#define Audio_Alaw_Val AFMT_A_LAW
+
+#include "audio-tbls.c"
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+static int debug;
+
+#define AUDIO_FILE_STRING "/dev/dsp"
+
+enum {
+ A_Pause,
+ A_UnPause
+};
+
+enum {
+ A_In,
+ A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static int audio_file = -1; /* file in/out */
+static int audio_file_in = -1; /* copy of above when opened O_READ/O_RDWR */
+static int audio_file_out = -1; /* copy of above when opened O_WRITE/O_RDWR */
+
+static int audio_swap_flag = 0; /* endian swap */
+
+static int audio_in_pause = A_UnPause;
+
+static Audio_t av;
+static int mixerleftvol[32];
+static int mixerrightvol[32];
+
+static int audio_enforce(Audio_t*);
+static int audio_open(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+ int i;
+ static ushort flag = 1;
+
+ audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */
+ audio_info_init(&av);
+ for (i = 0; i < 32; i++)
+ mixerleftvol[i] = mixerrightvol[i] = 100;
+}
+
+void
+audio_ctl_init(void)
+{
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ char ebuf[ERRMAX];
+
+ if (debug)
+ print("audio_file_open(0x%.8lux, %d)\n", c, omode);
+ switch(omode){
+ case OREAD:
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file;
+ poperror();
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ qunlock(&inlock);
+ nexterror();
+ }
+ if(audio_file_in >= 0 || audio_file_out >= 0)
+ error(Einuse);
+ if (audio_file < 0)
+ audio_file = audio_open();
+ audio_file_in = audio_file_out = audio_file;
+ poperror();
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+ if (debug)
+ print("audio_file_open: success\nin %d out %d both %d\n",
+ audio_file_out, audio_file_in, audio_file);
+}
+
+void
+audio_ctl_open(Chan *c, int omode)
+{
+ USED(c);
+ USED(omode);
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_out < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ qunlock(&outlock);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&inlock);
+ qlock(&outlock);
+ if (audio_file_in < 0) {
+ close(audio_file);
+ audio_file = -1;
+ }
+ audio_file_out = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ close(audio_file);
+ audio_file_in = audio_file_out = audio_file = -1;
+ qunlock(&outlock);
+ qunlock(&inlock);
+ break;
+ }
+}
+
+void
+audio_ctl_close(Chan *c)
+{
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long ba, status, chunk, total;
+ char *pva = (char *) va;
+
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(! audio_pause_in(audio_file_in, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count) {
+ chunk = count - total;
+ osenter();
+ status = read(audio_file_in, pva + total, chunk);
+ osleave();
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(pva, count);
+
+ poperror();
+ qunlock(&inlock);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ if (debug > 1)
+ print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
+ c, va, count, offset);
+
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(audio_file_out < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(va, count);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count) {
+ chunk = min(bufsz, count - total);
+ osenter();
+ status = write(audio_file_out, va, chunk);
+ osleave();
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ return count;
+}
+
+static int
+audio_open(void)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ if (debug)
+ print("audio_open: failed to set blocking\n");
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ if (debug)
+ print("audio_open: blocking set\n");
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = 0;
+
+ if(! audio_set_info(fd, &av.in, A_In)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ int fd;
+ int ff;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if (!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if (!audio_enforce(&tmpav))
+ error(Ebadarg);
+
+ qlock(&inlock);
+ if (waserror()) {
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+ if (!audio_pause_in(audio_file_in, A_Pause))
+ error(Ebadarg);
+ if (!audio_flush(audio_file_in, A_In))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&inlock);
+
+ qlock(&outlock);
+ if (waserror()) {
+ qunlock(&outlock);
+ nexterror();
+ }
+ if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+ if (!audio_pause_out(audio_file_out))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&outlock);
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+doioctl(int fd, int ctl, int *info)
+{
+ int status;
+ osenter();
+ status = ioctl(fd, ctl, info); /* qlock and load general stuff */
+ osleave();
+ if (status < 0)
+ print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
+ return status;
+}
+
+static int
+choosefmt(Audio_d *i)
+{
+ int newbits, newenc;
+
+ newbits = i->bits;
+ newenc = i->enc;
+ switch (newenc) {
+ case Audio_Alaw_Val:
+ if (newbits == 8)
+ return AFMT_A_LAW;
+ break;
+ case Audio_Ulaw_Val:
+ if (newbits == 8)
+ return AFMT_MU_LAW;
+ break;
+ case Audio_Pcm_Val:
+ if (newbits == 8)
+ return AFMT_U8;
+ else if (newbits == 16)
+ return AFMT_S16_LE;
+ break;
+ }
+ return -1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status;
+ int unequal_stereo = 0;
+
+ if(fd < 0)
+ return 0;
+
+ /* fmt */
+ if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
+ int oldfmt, newfmt;
+ oldfmt = AFMT_QUERY;
+ if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
+ return 0;
+ if (debug)
+ print("audio_set_info: current format 0x%.8lux\n", oldfmt);
+ newfmt = choosefmt(i);
+ if (debug)
+ print("audio_set_info: new format 0x%.8lux\n", newfmt);
+ if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
+ return 0;
+ }
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG) {
+ int channels = i->chan;
+ if (debug)
+ print("audio_set_info: new channels %d\n", channels);
+ if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
+ || channels != i->chan)
+ return 0;
+ }
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG) {
+ int speed = i->rate;
+ if (debug)
+ print("audio_set_info: new speed %d\n", speed);
+ if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
+ return 0;
+ }
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
+ int val;
+ if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
+ mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
+ if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
+ mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
+ val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
+ doioctl(fd, MIXER_WRITE(i->dev), &val);
+ }
+
+ if (i->flags & AUDIO_DEV_FLAG) {
+ }
+
+ return 1;
+}
+
+void
+audio_swap_endian(char *p, int n)
+{
+ int b;
+
+ while (n > 1) {
+ b = p[0];
+ p[0] = p[1];
+ p[1] = b;
+ p += 2;
+ n -= 2;
+ }
+}
+
+static int
+audio_pause_out(int fd)
+{
+ USED(fd);
+ return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+ USED(fd);
+ USED(f);
+ return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+ int x;
+ return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+ if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) &&
+ (t->in.rate != 8000 || t->in.chan != 1))
+ return 0;
+ if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) &&
+ (t->out.rate != 8000 || t->out.chan != 1))
+ return 0;
+ return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
--- /dev/null
+++ b/emu/OpenBSD/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgid(0, getpid());
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/OpenBSD/deveia.c
@@ -1,0 +1,39 @@
+/*
+ * FreeBSD serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/cuaa0",
+ "/dev/cuaa1",
+ "/dev/cuaa2",
+ "/dev/cuaa3",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {115200, B115200},
+ {230400, B230400},
+ {-1, -1}
+};
+
--- /dev/null
+++ b/emu/OpenBSD/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/OpenBSD/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/OpenBSD/mkfile
@@ -1,0 +1,45 @@
+<../../mkconfig
+SYSTARG=OpenBSD
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ win-x11a.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lm -lX11 -lXext -lossaudio
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/OpenBSD/mkfile-OpenBSD
@@ -1,0 +1,17 @@
+#
+# architecture-dependent files for OpenBSD
+#
+
+LDFLAGS=
+
+TARGFILES=devfs-posix.$O\
+ deveia-OpenBSD.$O\
+ devip.$O\
+ ipif-posix.$O\
+ os-OpenBSD.$O\
+ win-x11.$O\
+ srv.$O\
+ lock.$O\
+ asm-OpenBSD-$OBJTYPE.$O
+
+SYSLIBS=/usr/X11R6/lib/libX11.a -lm
--- /dev/null
+++ b/emu/OpenBSD/os.c
@@ -1,0 +1,533 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#undef getwd
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <time.h>
+#include <termios.h>
+#include <sched.h>
+#include <pwd.h>
+#include <errno.h>
+#include <unistd.h>
+
+enum
+{
+ DELETE = 0x7F,
+ NSTACKSPERALLOC = 16,
+ X11STACK= 256*1024
+};
+char *hosttype = "OpenBSD";
+
+int rfork_thread(int, void *, void (*)(void *), void *);
+
+extern void unlockandexit(int*);
+extern void executeonnewstack(void*, void (*f)(void*), void*);
+static void *stackalloc(Proc *p, void **tos);
+static void stackfreeandexit(void *stack);
+
+extern int dflag;
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+ Proc *p;
+ void *kstack;
+
+ lock(&procs.l);
+ p = up;
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(up->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ if(0)
+ print("pexit: %s: %s\n", up->text, msg);
+
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ kstack = p->kstack;
+ free(p->prog);
+ free(p);
+ if(kstack != nil)
+ stackfreeandexit(kstack);
+}
+
+void
+trapBUS(int signo, siginfo_t *info, void *context)
+{
+ if(info)
+ print("trapBUS: signo: %d code: %d addr: %lx\n",
+ info->si_signo, info->si_code, info->si_addr);
+ else
+ print("trapBUS: no info\n");
+ disfault(nil, "Bus error");
+}
+
+static void
+trapUSR1(int signo)
+{
+ int intwait;
+
+ USED(signo);
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+ if(intwait == 0) /* Not posted so its a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+static void
+trapUSR2(int signo)
+{
+ USED(signo);
+ /* we've done our work of interrupting sigsuspend */
+}
+
+static void
+trapILL(int signo)
+{
+ disfault(nil, "Illegal instruction");
+}
+
+static void
+trapSEGV(int signo)
+{
+ disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(int signo)
+{
+ char buf[64];
+ USED(signo);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
+ disfault(nil, buf);
+}
+
+static sigset_t initmask;
+
+static void
+setsigs(void)
+{
+ struct sigaction act;
+ sigset_t mask;
+
+ memset(&act, 0 , sizeof(act));
+ sigemptyset(&initmask);
+
+ signal(SIGPIPE, SIG_IGN); /* prevent signal when devcmd child exits */
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+
+ act.sa_handler = trapUSR1;
+ act.sa_mask = initmask;
+ sigaction(SIGUSR1, &act, nil);
+
+ act.sa_handler = trapUSR2;
+ sigaction(SIGUSR2, &act, nil);
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR2);
+ sigaddset(&initmask, SIGUSR2);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ /*
+ * prevent Zombies forming when any process terminates
+ */
+ act.sa_sigaction = 0;
+ act.sa_flags |= SA_NOCLDWAIT;
+ if(sigaction(SIGCHLD, &act, nil))
+ panic("sigaction SIGCHLD");
+
+ if(sflag == 0) {
+ act.sa_sigaction = trapBUS;
+ act.sa_flags |= SA_SIGINFO;
+ if(sigaction(SIGBUS, &act, nil))
+ panic("sigaction SIGBUS");
+ act.sa_handler = trapILL;
+ if(sigaction(SIGILL, &act, nil))
+ panic("sigaction SIGBUS");
+ act.sa_handler = trapSEGV;
+ if(sigaction(SIGSEGV, &act, nil))
+ panic("sigaction SIGSEGV");
+ act.sa_handler = trapFPE;
+ if(sigaction(SIGFPE, &act, nil))
+ panic("sigaction SIGFPE");
+ if(sigaddset(&initmask, SIGINT) == -1)
+ panic("sigaddset");
+ }
+ if(sigprocmask(SIG_BLOCK, &initmask, nil)!= 0)
+ panic("sigprocmask");
+}
+
+static void
+tramp(void *arg)
+{
+ Proc *p;
+
+ p = arg;
+ p->pid = p->sigid = getpid();
+ sigprocmask(SIG_BLOCK, &initmask, nil); /* in 5.3, rfork_thread doesn't copy from parent, contrary to docs? */
+ (*p->func)(p->arg);
+ pexit("{Tramp}", 0);
+ _exit(0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ int pid;
+ void *tos;
+
+ p = newproc();
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if(flags & KPX11){
+ p->kstack = nil; /* never freed; also up not defined */
+ tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*);
+ }else
+ p->kstack = stackalloc(p, &tos);
+ pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, tos, tramp, p);
+ if(pid < 0)
+ panic("rfork");
+}
+
+void
+oshostintr(Proc *p)
+{
+ kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ sigset_t mask;
+
+ sigprocmask(SIG_SETMASK, NULL, &mask);
+ sigdelset(&mask, SIGUSR2);
+ sigsuspend(&mask);
+}
+
+void
+osready(Proc *p)
+{
+ if(kill(p->sigid, SIGUSR2) < 0)
+ fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody()
+{
+ struct passwd *pwd;
+
+ if(pwd = getpwnam("nobody")) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+libinit(char *imod)
+{
+ struct passwd *pw;
+ Proc *p;
+ void *tos;
+ char sys[64];
+
+ setsid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ getnobody();
+
+ if(dflag == 0)
+ termset();
+
+ setsigs();
+
+ p = newproc();
+ p->kstack = stackalloc(p, &tos);
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ p->env->uid = getuid();
+ p->env->gid = getgid();
+
+ executeonnewstack(tos, emuinit, imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return 0;
+ if(sec0==0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec / 1000;
+ time.tv_nsec = (milsec % 1000) * 1000000;
+ nanosleep(&time, 0);
+ return 0;
+}
+
+void
+osyield(void)
+{
+ sched_yield();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+static struct {
+ Lock l;
+ void *free;
+} stacklist;
+
+static void
+_stackfree(void *stack)
+{
+ *((void **)stack) = stacklist.free;
+ stacklist.free = stack;
+}
+
+static void
+stackfreeandexit(void *stack)
+{
+ lock(&stacklist.l);
+ _stackfree(stack);
+ unlockandexit(&stacklist.l.val);
+}
+
+static void *
+stackalloc(Proc *p, void **tos)
+{
+ void *rv;
+ lock(&stacklist.l);
+ if (stacklist.free == 0) {
+ int x;
+ /*
+ * obtain some more by using sbrk()
+ */
+ void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
+ if (more == 0)
+ panic("stackalloc: no more stacks");
+ /*
+ * align to KSTACK
+ */
+ more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
+ /*
+ * free all the new stacks onto the freelist
+ */
+ for (x = 0; x < NSTACKSPERALLOC; x++)
+ _stackfree((char *)more + KSTACK * x);
+ }
+ rv = stacklist.free;
+ stacklist.free = *(void **)rv;
+ unlock(&stacklist.l);
+ *tos = rv + KSTACK - sizeof(void*);
+ *(Proc **)rv = p;
+ return rv;
+}
+
+int
+segflush(void *p, ulong n)
+{
+ return mprotect(p, n, PROT_EXEC|PROT_READ|PROT_WRITE);
+}
--- /dev/null
+++ b/emu/OpenBSD/rfork_thread.S
@@ -1,0 +1,104 @@
+/*-
+ * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * 8 12 16 20
+ * rfork_thread(flags, stack_addr, start_fnc, start_arg);
+ *
+ * flags: Flags to rfork system call. See rfork(2).
+ * stack_addr: Top of stack for thread.
+ * start_fnc: Address of thread function to call in child.
+ * start_arg: Argument to pass to the thread function in child.
+ */
+
+ENTRY(rfork_thread)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+
+ /*
+ * Push thread info onto the new thread's stack
+ */
+ movl 12(%ebp), %esi # get stack addr
+
+ subl $4, %esi
+ movl 20(%ebp), %eax # get start argument
+ movl %eax, (%esi)
+
+ subl $4, %esi
+ movl 16(%ebp), %eax # get start thread address
+ movl %eax, (%esi)
+
+ /*
+ * Prepare and execute the thread creation syscall
+ */
+ pushl 8(%ebp)
+ pushl $0
+ movl $SYS_rfork, %eax
+ int $0x80
+ jb 2f
+
+ /*
+ * Check to see if we are in the parent or child
+ */
+ cmpl $0, %edx
+ jnz 1f
+ addl $8, %esp
+ popl %esi
+ movl %ebp, %esp
+ popl %ebp
+ ret
+ .p2align 2
+
+ /*
+ * If we are in the child (new thread), then
+ * set-up the call to the internal subroutine. If it
+ * returns, then call __exit.
+ */
+1:
+ movl %esi,%esp
+ popl %eax
+ call *%eax
+ addl $4, %esp
+
+ /*
+ * Exit system call
+ */
+ pushl %eax
+ pushl $0
+ movl $SYS_threxit, %eax
+ int $0x80
+
+ /*
+ * Branch here if the thread creation fails:
+ */
+2:
+ addl $8, %esp
+ popl %esi
+ movl %ebp, %esp
+ popl %ebp
+ PIC_PROLOGUE
+ jmp PIC_PLT(_C_LABEL(__cerror))
--- /dev/null
+++ b/emu/Plan9/asm-386.s
@@ -1,0 +1,33 @@
+
+TEXT tramp(SB),$0
+ MOVL nsp+0(FP), BX /* new stack */
+ MOVL fn+4(FP), CX /* func to exec */
+ MOVL arg+8(FP),DX
+
+ LEAL -8(BX), SP /* new stack */
+ PUSHL DX
+ CALL *CX
+ POPL AX
+
+ PUSHL $0
+ CALL _exits(SB)
+ POPL AX
+ RET
+
+TEXT vstack(SB),$0
+ MOVL arg+0(FP), AX
+ MOVL ustack(SB), SP
+ PUSHL AX
+ CALL exectramp(SB)
+ POPL AX /* dammit ken! */
+ RET
+
+TEXT FPsave(SB), 1, $0
+ MOVL fpu+0(FP), AX
+ FSTENV 0(AX)
+ RET
+
+TEXT FPrestore(SB), 1, $0
+ MOVL fpu+0(FP), AX
+ FLDENV 0(AX)
+ RET
--- /dev/null
+++ b/emu/Plan9/asm-arm.s
@@ -1,0 +1,32 @@
+/* for VFP */
+#define VMRS(fp, cpu) WORD $(0xeef00a10 | (fp)<<16 | (cpu)<<12) /* FP → arm */
+#define VMSR(cpu, fp) WORD $(0xeee00a10 | (fp)<<16 | (cpu)<<12) /* arm → FP */
+
+#define Fpscr 1
+
+ TEXT tramp(SB), 1, $0
+ MOVW fn+4(FP), R1 /* func to exec */
+ MOVW arg+8(FP), R2 /* argument */
+ SUB $8, R0 /* new stack */
+ MOVW R0, SP
+ MOVW R2, R0
+ BL (R1)
+
+ MOVW $0, R0
+ BL _exits(SB)
+ RET
+
+ TEXT vstack(SB), 1, $0
+ MOVW ustack(SB), SP
+ BL exectramp(SB)
+ RET
+
+ TEXT FPsave(SB), 1, $0
+ VMRS(Fpscr, 1)
+ MOVW R1, 0(R0)
+ RET
+
+ TEXT FPrestore(SB), 1, $0
+ MOVW (R0), R0
+ VMSR(0, Fpscr)
+ RET
--- /dev/null
+++ b/emu/Plan9/asm-mips.s
@@ -1,0 +1,24 @@
+
+ TEXT tramp(SB), 1, $0
+ ADDU $-8, R1, R3 /* new stack */
+ MOVW 4(FP), R2 /* func to exec */
+ MOVW 8(FP), R1 /* arg to reg */
+ MOVW R3, R29 /* new stack */
+ JAL (R2)
+ MOVW R0, R1
+ JMP _exits(SB)
+
+ TEXT vstack(SB), 1, $0 /* Passes &targ through R1 */
+ MOVW ustack(SB), R29
+ JMP exectramp(SB)
+ RET
+
+ TEXT FPsave(SB), 1, $0
+ MOVW FCR31, R2
+ MOVW R2, 0(R1)
+ RET
+
+ TEXT FPrestore(SB), 1, $0
+ MOVW 0(R1), R2
+ MOVW R2, FCR31
+ RET
--- /dev/null
+++ b/emu/Plan9/asm-power.s
@@ -1,0 +1,28 @@
+ TEXT tramp(SB), 1, $0
+ ADD $-8, R3, R4 /* new stack */
+ MOVW 4(FP), R5 /* func to exec */
+ MOVW R5, LR
+ MOVW 8(FP), R3 /* arg to reg */
+ MOVW R4, R1 /* new stack */
+ BL (LR)
+ MOVW R0, R3
+ MOVW $_exits(SB), R4
+ MOVW R4, LR
+ BR (LR)
+
+ TEXT vstack(SB), 1, $0 /* Passes &targ through R3 */
+ MOVW ustack(SB), R1
+ MOVW $exectramp(SB), R4
+ MOVW R4, CTR
+ BR (CTR)
+ RETURN
+
+ TEXT FPsave(SB), 1, $0
+ MOVFL FPSCR, F0
+ FMOVD F0, 0(R3)
+ RETURN
+
+ TEXT FPrestore(SB), 1, $0
+ FMOVD 0(R3), F0
+ MOVFL F0, FPSCR
+ RETURN
--- /dev/null
+++ b/emu/Plan9/asm-sparc.s
@@ -1,0 +1,22 @@
+ TEXT tramp(SB), 1, $0
+ ADD $-8, R7, R3 /* new stack */
+ MOVW 4(FP), R4 /* func to exec */
+ MOVW 8(FP), R7 /* arg to reg */
+ MOVW R3, R1 /* new stack */
+ JMPL (R4)
+ MOVW R0, R7
+ JMPL _exits(SB) /* Leaks the stack in R29 */
+
+ TEXT vstack(SB), 1, $0 /* Passes &targ through R7 */
+ MOVW ustack(SB), R1
+ MOVW $exectramp(SB), R3
+ JMP (R3)
+ RETURN
+
+ TEXT FPsave(SB), 1, $0
+ MOVW FSR, 0(R7)
+ RETURN
+
+ TEXT FPrestore(SB), 1, $0
+ MOVW 0(R7), FSR
+ RETURN
--- /dev/null
+++ b/emu/Plan9/cmd.c
@@ -1,0 +1,189 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+extern void vstack(void*);
+
+/*
+ * all this is for the benefit of devcmd.
+ * i hope it's grateful.
+ */
+
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* standard input, output and error */
+ int wfd;
+ int* spin;
+ char** args;
+ char* dir;
+ int pid;
+ int nice;
+};
+
+/*
+ * called by vstack once it has moved to
+ * the unshared stack in the new process.
+ */
+void
+exectramp(Targ *t)
+{
+ int *fd, i, nfd;
+ char filename[128], err[ERRMAX], status[2*ERRMAX];
+
+ t->pid = getpid();
+ *t->spin = 0; /* allow parent to proceed: can't just rendezvous: see below */
+ fd = t->fd;
+
+ snprint(filename, sizeof(filename), "#d/%d", t->wfd);
+ t->wfd = open(filename, OWRITE|OCEXEC);
+ /* if it failed, we'll manage */
+
+ nfd = MAXNFD; /* TO DO: should read from /fd */
+ for(i = 0; i < nfd; i++)
+ if(i != fd[0] && i != fd[1] && i != fd[2] && i != t->wfd)
+ close(i);
+
+ if(fd[0] != 0){
+ dup(fd[0], 0);
+ close(fd[0]);
+ }
+ if(fd[1] != 1){
+ dup(fd[1], 1);
+ close(fd[1]);
+ }
+ if(fd[2] != 2){
+ dup(fd[2], 2);
+ close(fd[2]);
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ if(t->wfd > 0)
+ fprint(t->wfd, "chdir: %s: %r", t->dir);
+ _exits("bad dir");
+ }
+ if(t->nice)
+ oslopri();
+
+ exec(t->args[0], t->args);
+ err[0] = 0;
+ errstr(err, sizeof(err));
+ if(t->args[0][0] != '/' && t->args[0][0] != '#' &&
+ strncmp(t->args[0], "../", 3) != 0 && strncmp(t->args[0], "./", 2) != 0 &&
+ strlen(t->args[0])+5 < sizeof(filename)){
+ snprint(filename, sizeof(filename), "/bin/%s", t->args[0]);
+ exec(filename, t->args);
+ errstr(err, sizeof(err));
+ }
+ snprint(status, sizeof(status), "%s: can't exec: %s", t->args[0], err);
+ if(t->wfd > 0)
+ write(t->wfd, status, strlen(status));
+ _exits(status);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int spin, *spinptr, fd0[2], fd1[2], fd2[2], wfd[2], n;
+ Dir *d;
+
+ up->genbuf[0] = 0;
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+ t->args = args;
+ t->dir = dir;
+ t->nice = nice;
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(dir != nil){
+ d = dirstat(dir);
+ if(d == nil)
+ goto Error;
+ free(d);
+ }
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+
+ spinptr = &spin;
+ spin = 1;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->spin = spinptr;
+ switch(rfork(RFPROC|RFMEM|RFREND|RFNOTEG|RFFDG|RFNAMEG|RFENVG)) {
+ case -1:
+ goto Error;
+ case 0:
+ /* if child returns first from rfork, its call to vstack replaces ... */
+ vstack(t);
+ /* ... parent's return address from rfork and parent returns here */
+ default:
+ /* if parent returns first from rfork, it comes here */
+ /* can't call anything: on shared stack until child releases spin in exectramp */
+ while(*spinptr)
+ ;
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ up->genbuf[n] = 0;
+ errstr(up->genbuf, sizeof(up->genbuf));
+ free(t);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ errstr(up->genbuf, sizeof(up->genbuf)); /* save the message before close */
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ free(t);
+ errstr(up->genbuf, sizeof(up->genbuf));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ return postnote(PNGROUP, t->pid, "kill");
+}
+
+int
+oscmdwait(void*, char *buf, int n)
+{
+ return await(buf, n);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/Plan9/devfs.c
@@ -1,0 +1,365 @@
+/*
+ * Plan 9 file system interface
+ */
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+typedef struct Fsinfo Fsinfo;
+struct Fsinfo
+{
+ int fd;
+ QLock; /* serialise access to offset */
+ ulong offset; /* offset used only for directory reads */
+ Cname* name; /* Plan 9's name for file */
+ Qid rootqid; /* Plan 9's qid for Inferno's root */
+ char* root; /* prefix to strip from all names in diagnostics */
+};
+#define FS(c) ((Fsinfo*)((c)->aux))
+
+char rootdir[MAXROOT] = ROOT;
+
+static void
+fserr(Fsinfo *f)
+{
+ int n;
+ char *p;
+
+ oserrstr(up->env->errstr, ERRMAX);
+ if(f != nil && *up->env->errstr == '\'' && (n = strlen(f->root)) > 1){
+ /* don't reveal full names */
+ if(strncmp(up->env->errstr+1, f->root, n-1) == 0){
+ p = up->env->errstr+1+n;
+ memmove(up->env->errstr+1, p, strlen(p)+1);
+ }
+ }
+ error(up->env->errstr);
+}
+
+static void
+fsfree(Chan *c)
+{
+ cnameclose(FS(c)->name);
+ free(c->aux);
+}
+
+Chan*
+fsattach(char *spec)
+{
+ Chan *c;
+ Dir *d;
+ char *root;
+ Qid rootqid;
+ static int devno;
+ static Lock l;
+
+ if(!emptystr(spec)){
+ if(strcmp(spec, "*") != 0)
+ error(Ebadspec);
+ root = "/";
+ }else
+ root = rootdir;
+
+ d = dirstat(root);
+ if(d == nil)
+ fserr(nil);
+ rootqid = d->qid;
+ free(d);
+
+ c = devattach('U', spec);
+ lock(&l);
+ c->dev = devno++;
+ c->qid = rootqid;
+ unlock(&l);
+ c->aux = smalloc(sizeof(Fsinfo));
+ FS(c)->name = newcname(root);
+ FS(c)->rootqid = rootqid;
+ FS(c)->fd = -1;
+ FS(c)->root = root;
+
+ return c;
+}
+
+Walkqid*
+fswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ int j, alloc;
+ Walkqid *wq;
+ Dir *dir;
+ char *n;
+ Cname *current, *next;
+ Qid rootqid;
+
+ if(nname > 0)
+ isdir(c); /* do we need this? */
+
+ alloc = 0;
+ current = nil;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc && wq->clone!=nil)
+ cclose(wq->clone);
+ cnameclose(current);
+ free(wq);
+ return nil;
+ }
+ if(nc == nil){
+ nc = devclone(c);
+ nc->type = 0;
+ alloc = 1;
+ }
+ wq->clone = nc;
+
+ rootqid = FS(c)->rootqid;
+ current = FS(c)->name;
+ if(current != nil)
+ incref(¤t->r);
+ for(j=0; j<nname; j++){
+ if(!(nc->qid.type&QTDIR)){
+ if(j==0)
+ error(Enotdir);
+ break;
+ }
+ n = name[j];
+ if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){ /* TO DO: underlying qids aliased */
+ //print("** ufs walk '%s' -> %s\n", current->s, n);
+ next = current;
+ incref(&next->r);
+ next = addelem(current, n);
+ dir = dirstat(next->s);
+ if(dir == nil){
+ cnameclose(next);
+ if(j == 0)
+ error(Enonexist);
+ strcpy(up->env->errstr, Enonexist);
+ break;
+ }
+ nc->qid = dir->qid;
+ free(dir);
+ cnameclose(current);
+ current = next;
+ }
+ wq->qid[wq->nqid++] = nc->qid;
+ }
+// print("** ufs walk '%s'\n", current->s);
+
+ poperror();
+ if(wq->nqid < nname){
+ cnameclose(current);
+ if(alloc)
+ cclose(wq->clone);
+ wq->clone = nil;
+ }else if(wq->clone){
+ /* now attach to our device */
+ nc->aux = smalloc(sizeof(Fsinfo));
+ nc->type = c->type;
+ FS(nc)->rootqid = FS(c)->rootqid;
+ FS(nc)->name = current;
+ FS(nc)->fd = -1;
+ FS(nc)->root = FS(c)->root;
+ }else
+ panic("fswalk: can't happen");
+ return wq;
+}
+
+int
+fsstat(Chan *c, uchar *dp, int n)
+{
+ if(FS(c)->fd >= 0)
+ n = fstat(FS(c)->fd, dp, n);
+ else
+ n = stat(FS(c)->name->s, dp, n);
+ if(n < 0)
+ fserr(FS(c));
+ /* TO DO: change name to / if rootqid */
+ return n;
+}
+
+Chan*
+fsopen(Chan *c, int mode)
+{
+ osenter();
+ FS(c)->fd = open(FS(c)->name->s, mode);
+ osleave();
+ if(FS(c)->fd < 0)
+ fserr(FS(c));
+ c->mode = openmode(mode);
+ c->offset = 0;
+ FS(c)->offset = 0;
+ c->flag |= COPEN;
+ return c;
+}
+
+void
+fscreate(Chan *c, char *name, int mode, ulong perm)
+{
+ Dir *d;
+ Cname *n;
+
+ if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+ error(Efilename);
+ n = addelem(newcname(FS(c)->name->s), name);
+ osenter();
+ FS(c)->fd = create(n->s, mode, perm);
+ osleave();
+ if(FS(c)->fd < 0) {
+ cnameclose(n);
+ fserr(FS(c));
+ }
+ d = dirfstat(FS(c)->fd);
+ if(d == nil) {
+ cnameclose(n);
+ close(FS(c)->fd);
+ FS(c)->fd = -1;
+ fserr(FS(c));
+ }
+ c->qid = d->qid;
+ free(d);
+
+ cnameclose(FS(c)->name);
+ FS(c)->name = n;
+
+ c->mode = openmode(mode);
+ c->offset = 0;
+ FS(c)->offset = 0;
+ c->flag |= COPEN;
+}
+
+void
+fsclose(Chan *c)
+{
+ if(c->flag & COPEN){
+ osenter();
+ close(FS(c)->fd);
+ osleave();
+ }
+ /* don't need to check for CRCLOSE, because Plan 9 itself implements ORCLOSE */
+ fsfree(c);
+}
+
+static long
+fsdirread(Chan *c, void *va, long count, vlong offset)
+{
+ long n, r;
+ static char slop[16384];
+
+ if(FS(c)->offset != offset){
+ seek(FS(c)->fd, 0, 0);
+ for(n=0; n<offset;) {
+ r = offset - n;
+ if(r > sizeof(slop))
+ r = sizeof(slop);
+ osenter();
+ r = read(FS(c)->fd, slop, r);
+ osleave();
+ if(r <= 0){
+ FS(c)->offset = n;
+ return 0;
+ }
+ n += r;
+ }
+ FS(c)->offset = offset;
+ }
+ osenter();
+ r = read(FS(c)->fd, va, count);
+ osleave();
+ if(r < 0)
+ return r;
+ FS(c)->offset = offset+r;
+ return r;
+}
+
+long
+fsread(Chan *c, void *va, long n, vlong offset)
+{
+ int r;
+
+ if(c->qid.type & QTDIR){ /* need to maintain offset only for directories */
+ qlock(FS(c));
+ if(waserror()){
+ qunlock(FS(c));
+ nexterror();
+ }
+ r = fsdirread(c, va, n, offset);
+ poperror();
+ qunlock(FS(c));
+ }else{
+ osenter();
+ r = pread(FS(c)->fd, va, n, offset);
+ osleave();
+ }
+ if(r < 0)
+ fserr(FS(c));
+ return r;
+}
+
+long
+fswrite(Chan *c, void *va, long n, vlong offset)
+{
+ int r;
+
+ osenter();
+ r = pwrite(FS(c)->fd, va, n, offset);
+ osleave();
+ if(r < 0)
+ fserr(FS(c));
+ return r;
+}
+
+void
+fsremove(Chan *c)
+{
+ int r;
+
+ if(waserror()){
+ fsfree(c);
+ nexterror();
+ }
+ osenter();
+ r = remove(FS(c)->name->s);
+ osleave();
+ if(r < 0)
+ fserr(FS(c));
+ poperror();
+ fsfree(c);
+}
+
+int
+fswstat(Chan *c, uchar *dp, int n)
+{
+ osenter();
+ if(FS(c)->fd >= 0)
+ n = fwstat(FS(c)->fd, dp, n);
+ else
+ n = wstat(FS(c)->name->s, dp, n);
+ osleave();
+ if(n < 0)
+ fserr(FS(c));
+ return n;
+}
+
+void
+setid(char *name, int owner)
+{
+ if(!owner || iseve())
+ kstrdup(&up->env->user, name);
+}
+
+Dev fsdevtab = {
+ 'U',
+ "fs",
+
+ devinit,
+ fsattach,
+ fswalk,
+ fsstat,
+ fsopen,
+ fscreate,
+ fsclose,
+ fsread,
+ devbread,
+ fswrite,
+ devbwrite,
+ fsremove,
+ fswstat
+};
--- /dev/null
+++ b/emu/Plan9/devsrv9.c
@@ -1,0 +1,395 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+typedef struct Srv Srv;
+struct Srv
+{
+ Ref;
+ int fd; /* fd for opened /srv or /srv/X, or -1 */
+ int sfd; /* fd for created /srv entry or -1 */
+ uvlong path;
+ Srv *next;
+};
+
+static QLock srv9lk;
+static Srv *srv9;
+static Srv *srvroot;
+
+static char*
+srvname(Chan *c)
+{
+ char *p;
+
+ p = strrchr(c->name->s, '/');
+ if(p == nil)
+ return "";
+ return p+1;
+}
+
+static Srv*
+srvget(uvlong path)
+{
+ Srv *sv;
+
+ qlock(&srv9lk);
+ for(sv = srv9; sv != nil; sv = sv->next)
+ if(sv->path == path){
+ incref(sv);
+ qunlock(&srv9lk);
+ return sv;
+ }
+ sv = smalloc(sizeof(*sv));
+ sv->path = path;
+ sv->fd = -1;
+ sv->sfd = -1;
+ sv->ref = 1;
+ sv->next = srv9;
+ srv9 = sv;
+ qunlock(&srv9lk);
+ return sv;
+}
+
+static void
+srvput(Srv *sv)
+{
+ Srv **l;
+ int fd, sfd;
+
+ if(sv != nil && decref(sv) == 0){
+ qlock(&srv9lk);
+ for(l = &srv9; *l != nil; l = &(*l)->next)
+ if(*l == sv){
+ *l = sv->next;
+ break;
+ }
+ qunlock(&srv9lk);
+ fd = sv->fd;
+ sfd = sv->sfd;
+ free(sv);
+ if(sfd >= 0){
+ osenter();
+ close(sfd);
+ osleave();
+ }
+ if(fd >= 0){
+ osenter();
+ close(fd);
+ osleave();
+ }
+ }
+}
+
+static void
+srv9init(void)
+{
+ Srv *sv;
+
+ sv = mallocz(sizeof(*srvroot), 1);
+ sv->path = 0;
+ sv->fd = -1;
+ sv->ref = 1; /* subsequently never reaches zero */
+ srvroot = srv9 = sv;
+}
+
+static Chan*
+srv9attach(char *spec)
+{
+ Chan *c;
+
+ if(*spec)
+ error(Ebadspec);
+ c = devattach(L'₪', spec);
+ if(c != nil){
+ incref(srvroot);
+ c->aux = srvroot;
+ }
+ return c;
+}
+
+static Walkqid*
+srv9walk(Chan *c, Chan *nc, char **name, int nname)
+{
+ int j, alloc;
+ Walkqid *wq;
+ char *n;
+ Dir *d;
+
+ if(nname > 0)
+ isdir(c);
+
+ alloc = 0;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc)
+ cclose(wq->clone);
+ free(wq);
+ return nil;
+ }
+ if(nc == nil){
+ nc = devclone(c);
+ nc->type = 0; /* device doesn't know about this channel yet */
+ alloc = 1;
+ }
+ wq->clone = nc;
+
+ for(j=0; j<nname; j++){
+ if(!(nc->qid.type&QTDIR)){
+ if(j==0)
+ error(Enotdir);
+ break;
+ }
+ n = name[j];
+ if(strcmp(n, ".") != 0 && strcmp(n, "..") != 0){
+ snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", n);
+ d = dirstat(up->genbuf);
+ if(d == nil){
+ if(j == 0)
+ error(Enonexist);
+ kstrcpy(up->env->errstr, Enonexist, ERRMAX);
+ break;
+ }
+ nc->qid = d->qid;
+ free(d);
+ }
+ wq->qid[wq->nqid++] = nc->qid;
+ }
+ poperror();
+ if(wq->nqid < nname){
+ if(alloc)
+ cclose(wq->clone);
+ wq->clone = nil;
+ }else{
+ /* attach cloned channel to device */
+ wq->clone->type = c->type;
+ if(wq->clone != c)
+ nc->aux = srvget(nc->qid.path);
+ }
+ return wq;
+}
+
+static int
+srv9stat(Chan *c, uchar *db, int n)
+{
+ Srv *sv;
+ Dir d;
+
+ if(c->qid.type & QTDIR){
+ devdir(c, c->qid, "#₪", 0, eve, 0775, &d);
+ n = convD2M(&d, db, n);
+ if(n == 0)
+ error(Eshortstat);
+ return n;
+ }
+ sv = c->aux;
+ if(sv->fd >= 0){
+ osenter();
+ n = fstat(sv->fd, db, n);
+ osleave();
+ }else{
+ osenter();
+ n = stat(srvname(c), db, n);
+ osleave();
+ }
+ return n;
+}
+
+static Chan*
+srv9open(Chan *c, int omode)
+{
+ Srv *sv;
+ char *args[10];
+ int fd[2], i, ifd, is9p;
+ Dir *d;
+
+ sv = c->aux;
+ if(c->qid.type == QTDIR){
+ osenter();
+ sv->fd = open("/srv", omode);
+ osleave();
+ if(sv->fd < 0)
+ oserror();
+ c->mode = omode;
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+
+ if(omode&OTRUNC || openmode(omode) != ORDWR)
+ error(Eperm);
+ if(sv->fd < 0){
+ snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", srvname(c));
+
+ /* check permission */
+ osenter();
+ ifd = open(up->genbuf, omode);
+ osleave();
+ if(ifd < 0)
+ oserror();
+ osenter();
+ d = dirfstat(ifd);
+ is9p = d != nil && d->qid.type & QTMOUNT;
+ free(d);
+ osleave();
+
+ if(is9p){
+ close(ifd);
+
+ /* spawn exportfs */
+ args[0] = "exportfs";
+ args[1] = "-S";
+ args[2] = up->genbuf;
+ args[3] = nil;
+ if(pipe(fd) < 0)
+ oserror();
+ /* TO DO: without RFMEM there's a copy made of each page touched by any kproc until the exec */
+ switch(rfork(RFPROC|RFNOWAIT|RFREND|RFFDG|RFNAMEG|RFENVG)){ /* no sharing except NOTEG */
+ case -1:
+ oserrstr(up->genbuf, sizeof(up->genbuf));
+ close(fd[0]);
+ close(fd[1]);
+ error(up->genbuf);
+ case 0:
+ for(i=3; i<MAXNFD; i++)
+ if(i != fd[1])
+ close(i);
+ dup(fd[1], 0);
+ if(fd[0] != 0)
+ close(fd[0]);
+ dup(0, 1);
+ exec("/bin/exportfs", args);
+ exits("exportfs failed");
+ default:
+ sv->fd = fd[0];
+ close(fd[1]);
+ break;
+ }
+ }else
+ sv->fd = ifd;
+ }
+
+ c->mode = ORDWR;
+ c->offset = 0;
+ c->flag |= COPEN;
+ return c;
+}
+
+static void
+srv9close(Chan *c)
+{
+ srvput(c->aux);
+}
+
+static long
+srv9read(Chan *c, void *va, long n, vlong off)
+{
+ Srv *sv;
+
+ sv = c->aux;
+ osenter();
+ n = pread(sv->fd, va, n, off);
+ osleave();
+ if(n < 0)
+ oserror();
+ return n;
+}
+
+static long
+srv9write(Chan *c, void *va, long n, vlong off)
+{
+ Srv *sv;
+
+ sv = c->aux;
+ osenter();
+ n = pwrite(sv->fd, va, n, off);
+ osleave();
+ if(n == 0)
+ error(Ehungup);
+ if(n < 0)
+ oserror();
+ return n;
+}
+
+static void
+srv9create(Chan *c, char *name, int omode, ulong perm)
+{
+ Srv *sv;
+ int sfd, fd[2];
+ vlong path;
+ Dir *d;
+
+ if(openmode(omode) != ORDWR)
+ error(Eperm);
+
+ if(pipe(fd) < 0)
+ oserror();
+ if(waserror()){
+ close(fd[0]);
+ close(fd[1]);
+ nexterror();
+ }
+
+ snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", name);
+ osenter();
+ sfd = create(up->genbuf, OWRITE|ORCLOSE, perm);
+ osleave();
+ if(sfd < 0)
+ oserror();
+ if(waserror()){
+ close(sfd);
+ nexterror();
+ }
+ osenter();
+ if(fprint(sfd, "%d", fd[1]) < 0){
+ osleave();
+ oserror();
+ }
+ d = dirfstat(sfd);
+ osleave();
+ if(d != nil){
+ path = d->qid.path;
+ free(d);
+ }else
+ oserror();
+
+ poperror();
+ poperror();
+ close(fd[1]);
+
+ if(waserror()){
+ close(sfd);
+ close(fd[0]);
+ nexterror();
+ }
+ sv = srvget(path);
+ sv->fd = fd[0];
+ sv->sfd = sfd;
+ poperror();
+
+ srvput((Srv*)c->aux);
+ c->qid.type = QTFILE;
+ c->qid.path = path;
+ c->aux = sv;
+ c->flag |= COPEN;
+ c->mode = ORDWR;
+ c->offset = 0;
+}
+
+Dev srv9devtab = {
+ L'₪',
+ "srv9",
+
+ srv9init,
+ srv9attach,
+ srv9walk,
+ srv9stat,
+ srv9open,
+ srv9create, /* TO DO */
+ srv9close,
+ srv9read,
+ devbread,
+ srv9write,
+ devbwrite,
+ devremove, /* TO DO */
+ devwstat, /* TO DO */
+};
--- /dev/null
+++ b/emu/Plan9/emu
@@ -1,0 +1,114 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+# sign
+
+ draw win
+ pointer
+
+ dynld
+ mem
+ srv9
+
+# ip and eia are simply bound in from Plan 9
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+ dynld
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+# srv not used on Plan 9
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ exptab
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /chan /
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /nvfs /
+ /env /
+ /root /
+ /srv /
+# /tmp /
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Plan9/emusig
@@ -1,0 +1,100 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+ sign
+
+ draw win
+
+# ip and eia are simply bound in from Plan 9
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+ memlayer
+ memdraw
+ keyring
+ crypt
+ 9
+
+link
+
+mod
+ sys
+ draw
+ tk
+ math
+# srv not used on Plan 9
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Plan9/mkfile
@@ -1,0 +1,45 @@
+SYSTARG=Plan9
+OBJTYPE=$objtype
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ $DEVS\
+ $PORT\
+
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+safeinstall:V: $O.$CONF
+ mv $INSTALLDIR/$CONF $INSTALLDIR/$CONF.`{date -n}
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
--- /dev/null
+++ b/emu/Plan9/os.c
@@ -1,0 +1,428 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ KSTACK = 16*1024,
+ DELETE = 0x7F,
+};
+
+Proc **Xup;
+
+extern void killrefresh(void);
+extern void tramp(char*, void (*)(void*), void*);
+
+extern int usenewwin;
+
+int *ustack; /* address on unshared stack: see vstack in asm*.s */
+extern int dflag;
+char *hosttype = "Plan9";
+char *cputype;
+
+void
+osblock(void)
+{
+ rendezvous(up, nil);
+}
+
+void
+osready(Proc *p)
+{
+ rendezvous(p, nil);
+}
+
+void
+pexit(char *msg, int)
+{
+ Osenv *e;
+
+ USED(msg);
+
+ lock(&procs.l);
+ if(up->prev)
+ up->prev->next = up->next;
+ else
+ procs.head = up->next;
+
+ if(up->next)
+ up->next->prev = up->prev;
+ else
+ procs.tail = up->prev;
+ unlock(&procs.l);
+
+/* print("pexit: %s: %s\n", up->text, msg); /**/
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(e->user);
+ free(up->prog);
+ up->prog = nil;
+ up->type = Moribund;
+ longjmp(up->privstack, 1);
+}
+
+int
+kproc1(char *name, void (*func)(void*), void *arg, int flags)
+{
+ int pid;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+
+ p = newproc();
+ if(p == nil)
+ panic("kproc: no memory");
+ p->kstack = mallocz(KSTACK, 0);
+ if(p->kstack == nil)
+ panic("kproc: no memory");
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ /*
+ * switch back to the unshared stack to do the fork
+ * only the parent returns from kproc
+ */
+ up->kid = p;
+ up->kidsp = p->kstack;
+ pid = setjmp(up->sharestack);
+ if(pid == 0)
+ longjmp(up->privstack, 1);
+ return pid;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ kproc1(name, func, arg, flags);
+}
+
+void
+traphandler(void *reg, char *msg)
+{
+ int intwait;
+
+ intwait = up->intwait;
+ up->intwait = 0;
+ /* Ignore pipe writes from devcmd */
+ if(strstr(msg, "write on closed pipe") != nil)
+ noted(NCONT);
+
+ if(sflag) {
+ if(intwait && strcmp(msg, Eintr) == 0)
+ noted(NCONT);
+ else
+ noted(NDFLT);
+ }
+ if(intwait == 0)
+ disfault(reg, msg);
+ noted(NCONT);
+}
+
+int
+readfile(char *path, char *buf, int n)
+{
+ int fd;
+
+ fd = open(path, OREAD);
+ if(fd >= 0) {
+ n = read(fd, buf, n-1);
+ if(n > 0) /* both calls to readfile() have a ``default'' */
+ buf[n] = '\0';
+ close(fd);
+ return n;
+ }
+ return 0;
+}
+
+static void
+dobinds(void)
+{
+ char dir[MAXROOT+9];
+
+ snprint(dir, sizeof(dir), "%s/net", rootdir);
+ bind("/net", dir, MREPL);
+
+ snprint(dir, sizeof(dir), "%s/net.alt", rootdir);
+ bind("/net.alt", dir, MREPL);
+
+ snprint(dir, sizeof(dir), "%s/dev", rootdir);
+ bind("#t", dir, MAFTER);
+ bind("#A", dir, MAFTER);
+}
+
+void
+libinit(char *imod)
+{
+ char *sp;
+ Proc *xup, *p;
+ int fd, n, pid;
+ char nbuf[64];
+
+ xup = nil;
+ Xup = &xup;
+
+ /*
+ * setup personality
+ */
+ if(readfile("/dev/user", nbuf, sizeof nbuf))
+ kstrdup(&eve, nbuf);
+ if(readfile("/dev/sysname", nbuf, sizeof nbuf))
+ kstrdup(&ossysname, nbuf);
+ if(readfile("/env/cputype", nbuf, sizeof nbuf))
+ kstrdup(&cputype, nbuf);
+
+ /*
+ * guess at a safe stack for vstack
+ */
+ ustack = &fd;
+
+ rfork(RFNAMEG|RFREND);
+
+ if(!dflag){
+ fd = open("/dev/consctl", OWRITE);
+ if(fd < 0)
+ fprint(2, "libinit: open /dev/consctl: %r\n");
+ n = write(fd, "rawon", 5);
+ if(n != 5)
+ fprint(2, "keyboard rawon (n=%d, %r)\n", n);
+ }
+
+ osmillisec(); /* set the epoch */
+ dobinds();
+
+ notify(traphandler);
+
+ /*
+ * dummy up a up and stack so the first proc
+ * calls emuinit after setting up his private jmp_buf
+ */
+ p = newproc();
+ p->kstack = mallocz(KSTACK, 0);
+ if(p == nil || p->kstack == nil)
+ panic("libinit: no memory");
+ sp = p->kstack;
+ p->func = emuinit;
+ p->arg = imod;
+
+ /*
+ * set up a stack for forking kids on separate stacks.
+ * longjmp back here from kproc.
+ */
+ while(setjmp(p->privstack)){
+ if(up->type == Moribund){
+ free(up->kstack);
+ free(up);
+ _exits("");
+ }
+ p = up->kid;
+ sp = up->kidsp;
+ up->kid = nil;
+ switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
+ case 0:
+ /*
+ * send the kid around the loop to set up his private jmp_buf
+ */
+ break;
+ default:
+ /*
+ * parent just returns to his shared stack in kproc
+ */
+ longjmp(up->sharestack, pid);
+ panic("longjmp failed");
+ }
+ }
+
+ /*
+ * you get here only once per Proc
+ * go to the shared memory stack
+ */
+ up = p;
+ up->pid = up->sigid = getpid();
+ tramp(sp+KSTACK, up->func, up->arg);
+ panic("tramp returned");
+}
+
+void
+oshostintr(Proc *p)
+{
+ postnote(PNPROC, p->sigid, Eintr);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ if(regs != nil)
+ notejmp(regs, env, val);
+ else
+ longjmp(env, val);
+}
+
+void
+osreboot(char*, char**)
+{
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+ killrefresh();
+ postnote(PNGROUP, getpid(), "interrupt");
+ exits("interrupt");
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ fprint(2, "emu: keyboard read error: %r\n");
+ if(n <= 0)
+ pexit("keyboard", 0);
+ switch(buf[0]) {
+ case DELETE:
+ cleanexit(0);
+ case '\r':
+ buf[0] = '\n';
+ }
+ return buf[0];
+}
+
+static vlong
+b2v(uchar *p)
+{
+ int i;
+ vlong v;
+
+ v = 0;
+ for(i=0; i<sizeof(uvlong); i++)
+ v = (v<<8)|p[i];
+ return v;
+}
+
+vlong
+nsec(void)
+{
+ int n;
+ static int nsecfd = -1;
+ uchar buf[sizeof(uvlong)];
+
+ if(nsecfd < 0){
+ nsecfd = open("/dev/bintime", OREAD|OCEXEC); /* never closed */
+ if(nsecfd<0){
+ fprint(2,"can't open /dev/bintime: %r\n");
+ return 0;
+ }
+ }
+ n = read(nsecfd, buf, sizeof(buf));
+ if(n!=sizeof(buf)) {
+ fprint(2,"read err on /dev/bintime: %r\n");
+ return 0;
+ }
+ return b2v(buf);
+}
+
+long
+osmillisec(void)
+{
+ static vlong nsec0 = 0;
+
+ if(nsec0 == 0){
+ nsec0 = nsec();
+ return 0;
+ }
+ return (nsec()-nsec0)/1000000;
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osusectime(void)
+{
+ return nsec()/1000;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ sleep(milsec);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ sleep(0);
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ sleep(1000000);
+}
+
+void
+oslopri(void)
+{
+ int fd;
+ char buf[32];
+
+ snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
+ if((fd = open(buf, OWRITE)) >= 0){
+ fprint(fd, "pri 8");
+ close(fd);
+ }
+}
--- /dev/null
+++ b/emu/Plan9/win.c
@@ -1,0 +1,566 @@
+#include "dat.h"
+#include "fns.h"
+#include "kernel.h"
+#include "error.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "keyboard.h"
+
+enum
+{
+ Margin = 4,
+ Lsize = 100,
+};
+
+extern Memimage *screenimage;
+
+extern int kproc1(char*, void (*)(void*), void*, int);
+
+static ulong* attachwindow(Rectangle*, ulong*, int*, int*);
+
+static void plan9readmouse(void*);
+static void plan9readkeybd(void*);
+static int mapspecials(char *s1, char *s2, int *n);
+
+int usenewwin = 1;
+int kbdiscons;
+static int truedepth;
+
+static int datafd;
+static int ctlfd;
+static int mousefd = -1;
+static int keybdfd;
+static int mousepid = -1;
+static int keybdpid = -1;
+static int cursfd;
+static char winname[64];
+
+/* Following updated by attachwindow() asynchronously */
+static QLock ql;
+static Rectangle tiler;
+static ulong* data;
+static uchar* loadbuf;
+static int cursfd;
+static int imageid;
+static Rectangle imager;
+static uchar *chunk;
+static int chunksize;
+static int dispbufsize;
+
+#define NINFO 12*12
+#define HDR 21
+
+
+void
+killrefresh(void)
+{
+ if(mousepid < 0)
+ return;
+ close(mousefd);
+ close(ctlfd);
+ close(datafd);
+ postnote(PNPROC, mousepid, Eintr);
+ postnote(PNPROC, keybdpid, Eintr);
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+ int fd;
+ char *p, buf[128], info[NINFO+1];
+
+ if(usenewwin){
+ p = getenv("wsys");
+ if(p == nil)
+ return nil;
+
+ fd = open(p, ORDWR);
+ if(fd < 0) {
+ fprint(2, "attachscreen: can't open window manager: %r\n");
+ return nil;
+ }
+ sprint(buf, "new -dx %d -dy %d", Xsize+2*Margin, Ysize+2*Margin);
+ if(mount(fd, -1, "/mnt/wsys", MREPL, buf) < 0) {
+ fprint(2, "attachscreen: can't mount window manager: %r\n");
+ return nil;
+ }
+ if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){
+ fprint(2, "attachscreen: can't bind /mnt/wsys before /dev: %r\n");
+ return nil;
+ }
+ }
+
+ cursfd = open("/dev/cursor", OWRITE);
+ if(cursfd < 0) {
+ fprint(2, "attachscreen: open cursor: %r\n");
+ return nil;
+ }
+
+ /* Set up graphics window console (chars->gkbdq) */
+ keybdfd = open("/dev/cons", OREAD);
+ if(keybdfd < 0) {
+ fprint(2, "attachscreen: open keyboard: %r\n");
+ return nil;
+ }
+ mousefd = open("/dev/mouse", ORDWR);
+ if(mousefd < 0){
+ fprint(2, "attachscreen: can't open mouse: %r\n");
+ return nil;
+ }
+ if(usenewwin || 1){
+ fd = open("/dev/consctl", OWRITE);
+ if(fd < 0)
+ fprint(2, "attachscreen: open /dev/consctl: %r\n");
+ if(write(fd, "rawon", 5) != 5)
+ fprint(2, "attachscreen: write /dev/consctl: %r\n");
+ }
+
+ /* Set up graphics files */
+ ctlfd = open("/dev/draw/new", ORDWR);
+ if(ctlfd < 0){
+ fprint(2, "attachscreen: can't open graphics control file: %r\n");
+ return nil;
+ }
+ if(read(ctlfd, info, sizeof info) < NINFO){
+ close(ctlfd);
+ fprint(2, "attachscreen: can't read graphics control file: %r\n");
+ return nil;
+ }
+ sprint(buf, "/dev/draw/%d/data", atoi(info+0*12));
+ datafd = open(buf, ORDWR|OCEXEC);
+ if(datafd < 0){
+ close(ctlfd);
+ fprint(2, "attachscreen: can't read graphics data file: %r\n");
+ return nil;
+ }
+ dispbufsize = iounit(datafd);
+ if(dispbufsize <= 0)
+ dispbufsize = 8000;
+ if(dispbufsize < 512){
+ close(ctlfd);
+ close(datafd);
+ fprint(2, "attachscreen: iounit %d too small\n", dispbufsize);
+ return nil;
+ }
+ chunksize = dispbufsize - 64;
+
+ if(attachwindow(r, chan, d, width) == nil){
+ close(ctlfd);
+ close(datafd);
+ return nil;
+ }
+
+ mousepid = kproc1("readmouse", plan9readmouse, nil, 0);
+ keybdpid = kproc1("readkbd", plan9readkeybd, nil, 0);
+
+ fd = open("/dev/label", OWRITE);
+ if(fd >= 0){
+ snprint(buf, sizeof(buf), "inferno %d", getpid());
+ write(fd, buf, strlen(buf));
+ close(fd);
+ }
+
+ *softscreen = 1;
+ return (uchar*)data;
+}
+
+static ulong*
+attachwindow(Rectangle *r, ulong *chan, int *d, int *width)
+{
+ int n, fd, nb;
+ char buf[256];
+ uchar ubuf[128];
+ ulong imagechan;
+
+ /*
+ * Discover name of window
+ */
+ fd = open("/mnt/wsys/winname", OREAD);
+ if(fd<0 || (n=read(fd, winname, sizeof winname))<=0){
+ fprint(2, "attachwindow: can only run inferno under rio, not stand-alone\n");
+ return nil;
+ }
+ close(fd);
+ /*
+ * If had previous window, release it
+ */
+ if(imageid > 0){
+ ubuf[0] = 'f';
+ BPLONG(ubuf+1, imageid);
+ if(write(datafd, ubuf, 1+4) != 1+4)
+ fprint(2, "attachwindow: cannot free old window: %r\n");
+ }
+ /*
+ * Allocate image pointing to window, and discover its ID
+ */
+ ubuf[0] = 'n';
+ ++imageid;
+ BPLONG(ubuf+1, imageid);
+ ubuf[5] = n;
+ memmove(ubuf+6, winname, n);
+ if(write(datafd, ubuf, 6+n) != 6+n){
+ fprint(2, "attachwindow: cannot bind %d to window id '%s': %r\n", imageid, winname);
+ return nil;
+ }
+ if(read(ctlfd, buf, sizeof buf) < 12*12){
+ fprint(2, "attachwindow: cannot read window id: %r\n");
+ return nil;
+ }
+ imagechan = strtochan(buf+2*12);
+ truedepth = chantodepth(imagechan);
+ if(truedepth == 0){
+ fprint(2, "attachwindow: cannot handle window depth specifier %.12s\n", buf+2*12);
+ return nil;
+ }
+
+ /*
+ * Report back
+ */
+ if(chan != nil)
+ *chan = imagechan;
+ if(d != nil)
+ *d = chantodepth(imagechan);
+ nb = 0;
+ if(r != nil){
+ Xsize = atoi(buf+6*12)-atoi(buf+4*12)-2*Margin;
+ Ysize = atoi(buf+7*12)-atoi(buf+5*12)-2*Margin;
+ r->min.x = 0;
+ r->min.y = 0;
+ r->max.x = Xsize;
+ r->max.y = Ysize;
+ nb = bytesperline(*r, truedepth);
+ data = malloc(nb*Ysize);
+ loadbuf = malloc(nb*Lsize+1);
+ chunk = malloc(HDR+chunksize+5); /* +5 for flush (1 old, 5 new) */
+ }
+ imager.min.x = atoi(buf+4*12);
+ imager.min.y = atoi(buf+5*12);
+ imager.max.x = atoi(buf+6*12);
+ imager.max.y = atoi(buf+7*12);
+
+ if(width != nil)
+ *width = nb/4;
+
+ tiler.min.x = atoi(buf+4*12)+Margin;
+ tiler.min.y = atoi(buf+5*12)+Margin;
+ tiler.max.x = atoi(buf+6*12)-Margin;
+ tiler.max.y = atoi(buf+7*12)-Margin;
+
+ return data;
+}
+
+static int
+plan9loadimage(Rectangle r, uchar *data, int ndata)
+{
+ long dy;
+ int n, bpl;
+
+ if(!rectinrect(r, imager)){
+ werrstr("loadimage: bad rectangle");
+ return -1;
+ }
+ bpl = bytesperline(r, truedepth);
+ n = bpl*Dy(r);
+ if(n > ndata){
+ werrstr("loadimage: insufficient data");
+ return -1;
+ }
+ ndata = 0;
+ while(r.max.y > r.min.y){
+ dy = r.max.y - r.min.y;
+ if(dy*bpl> chunksize)
+ dy = chunksize/bpl;
+ n = dy*bpl;
+ chunk[0] = 'y';
+ BPLONG(chunk+1, imageid);
+ BPLONG(chunk+5, r.min.x);
+ BPLONG(chunk+9, r.min.y);
+ BPLONG(chunk+13, r.max.x);
+ BPLONG(chunk+17, r.min.y+dy);
+ memmove(chunk+21, data, n);
+ ndata += n;
+ data += n;
+ r.min.y += dy;
+ n += 21;
+ if(r.min.y >= r.max.y) /* flush to screen */
+ chunk[n++] = 'v';
+ if(write(datafd, chunk, n) != n)
+ return -1;
+ }
+ return ndata;
+}
+
+static void
+_flushmemscreen(Rectangle r)
+{
+ int n, dy, l;
+ Rectangle rr;
+
+ if(data == nil || loadbuf == nil || chunk==nil)
+ return;
+ if(!rectclip(&r, Rect(0, 0, Xsize, Ysize)))
+ return;
+ if(!rectclip(&r, Rect(0, 0, Dx(tiler), Dy(tiler))))
+ return;
+ if(Dx(r)<=0 || Dy(r)<=0)
+ return;
+ l = bytesperline(r, truedepth);
+ while(r.min.y < r.max.y){
+ dy = Dy(r);
+ if(dy > Lsize)
+ dy = Lsize;
+ rr = r;
+ rr.max.y = rr.min.y+dy;
+ n = unloadmemimage(screenimage, rr, loadbuf, l*dy);
+ /* offset from (0,0) to window */
+ rr.min.x += tiler.min.x;
+ rr.min.y += tiler.min.y;
+ rr.max.x += tiler.min.x;
+ rr.max.y += tiler.min.y;
+ if(plan9loadimage(rr, loadbuf, n) != n)
+ fprint(2, "flushmemscreen: %d bytes: %r\n", n);
+ r.min.y += dy;
+ }
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ qlock(&ql);
+ _flushmemscreen(r);
+ qunlock(&ql);
+}
+
+void
+drawcursor(Drawcursor *c)
+{
+ int j, i, h, w, bpl;
+ uchar *bc, *bs, *cclr, *cset, curs[2*4+2*2*16];
+
+ /* Set the default system cursor */
+ if(c->data == nil) {
+ write(cursfd, curs, 0);
+ return;
+ }
+
+ BPLONG(curs+0*4, c->hotx);
+ BPLONG(curs+1*4, c->hoty);
+
+ w = (c->maxx-c->minx);
+ h = (c->maxy-c->miny)/2;
+
+ cclr = curs+2*4;
+ cset = curs+2*4+2*16;
+ bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+ bc = c->data;
+ bs = c->data + h*bpl;
+
+ if(h > 16)
+ h = 16;
+ if(w > 16)
+ w = 16;
+ w /= 8;
+ for(i = 0; i < h; i++) {
+ for(j = 0; j < w; j++) {
+ cclr[j] = bc[j];
+ cset[j] = bs[j];
+ }
+ bc += bpl;
+ bs += bpl;
+ cclr += 2;
+ cset += 2;
+ }
+ write(cursfd, curs, sizeof curs);
+}
+
+static int
+checkmouse(char *buf, int n)
+{
+ int x, y, tick, b;
+ static int lastb, lastt, lastx, lasty, lastclick;
+
+ switch(n){
+ default:
+ kwerrstr("atomouse: bad count");
+ return -1;
+
+ case 1+4*12:
+ if(buf[0] == 'r'){
+ qlock(&ql);
+ if(attachwindow(nil, nil, nil, nil) == nil) {
+ qunlock(&ql);
+ return -1;
+ }
+ _flushmemscreen(Rect(0, 0, Xsize, Ysize));
+ qunlock(&ql);
+ }
+ x = atoi(buf+1+0*12) - tiler.min.x;
+ y = atoi(buf+1+1*12) - tiler.min.y;
+ b = atoi(buf+1+2*12);
+ tick = atoi(buf+1+3*12);
+ if(b && lastb == 0){ /* button newly pressed */
+ if(b==lastclick && tick-lastt<400
+ && abs(x-lastx)<10 && abs(y-lasty)<10)
+ b |= (1<<8);
+ lastt = tick;
+ lastclick = b&0xff;
+ lastx = x;
+ lasty = y;
+ }
+ lastb = b&0xff;
+ //mouse.msec = tick;
+ mousetrack(b, x, y, 0);
+ return n;
+ }
+}
+
+static void
+plan9readmouse(void *v)
+{
+ int n;
+ char buf[128];
+
+ USED(v);
+ for(;;){
+ n = read(mousefd, buf, sizeof(buf));
+ if(n < 0) /* probably interrupted */
+ _exits(0);
+ checkmouse(buf, n);
+ }
+}
+
+static void
+plan9readkeybd(void*)
+{
+ int n, partial;
+ char buf[32];
+ char dbuf[32 * 3]; /* overestimate but safe */
+
+ partial = 0;
+ for(;;){
+ n = read(keybdfd, buf + partial, sizeof(buf) - partial);
+ if(n < 0) /* probably interrupted */
+ _exits(0);
+ partial += n;
+ n = mapspecials(dbuf, buf, &partial);
+ qproduce(gkbdq, dbuf, n);
+ }
+}
+
+void
+setpointer(int x, int y)
+{
+ char buf[50];
+ int n;
+
+ if(mousefd < 0)
+ return;
+ x += tiler.min.x;
+ y += tiler.min.y;
+ n = snprint(buf, sizeof buf, "m%11d %11d ", x, y);
+ write(mousefd, buf, n);
+}
+
+/*
+ * plan9 keyboard codes; from /sys/include/keyboard.h; can't include directly
+ * because constant names clash.
+ */
+enum {
+ P9KF= 0xF000, /* Rune: beginning of private Unicode space */
+ P9Spec= 0xF800,
+ /* KF|1, KF|2, ..., KF|0xC is F1, F2, ..., F12 */
+ Khome= P9KF|0x0D,
+ Kup= P9KF|0x0E,
+ Kpgup= P9KF|0x0F,
+ Kprint= P9KF|0x10,
+ Kleft= P9KF|0x11,
+ Kright= P9KF|0x12,
+ Kdown= P9Spec|0x00,
+ Kview= P9Spec|0x00,
+ Kpgdown= P9KF|0x13,
+ Kins= P9KF|0x14,
+ Kend= KF|0x18,
+
+ Kalt= P9KF|0x15,
+ Kshift= P9KF|0x16,
+ Kctl= P9KF|0x17,
+};
+
+/*
+ * translate plan 9 special characters from s2 (of length *n) into s1;
+ * return number of chars placed into s1.
+ * any trailing incomplete chars are moved to the beginning of s2,
+ * and *n set to the number moved there.
+ */
+static int
+mapspecials(char *s1, char *s2, int *n)
+{
+ char *s, *d, *es2;
+ Rune r;
+ d = s1;
+ s = s2;
+ es2 = s2 + *n;
+ while (fullrune(s, es2 - s)) {
+ s += chartorune(&r, s);
+ switch (r) {
+ case Kshift:
+ r = LShift;
+ break;
+ case Kctl:
+ r = LCtrl;
+ break;
+ case Kalt:
+ r = LAlt;
+ break;
+ case Khome:
+ r = Home;
+ break;
+ case Kend:
+ r = End;
+ break;
+ case Kup:
+ r = Up;
+ break;
+ case Kdown:
+ r = Down;
+ break;
+ case Kleft:
+ r = Left;
+ break;
+ case Kright:
+ r = Right;
+ break;
+ case Kpgup:
+ r = Pgup;
+ break;
+ case Kpgdown:
+ r = Pgdown;
+ break;
+ case Kins:
+ r = Ins;
+ break;
+ /*
+ * function keys
+ */
+ case P9KF|1:
+ case P9KF|2:
+ case P9KF|3:
+ case P9KF|4:
+ case P9KF|5:
+ case P9KF|6:
+ case P9KF|7:
+ case P9KF|8:
+ case P9KF|9:
+ case P9KF|10:
+ case P9KF|11:
+ case P9KF|12:
+ r = (r - P9KF) + KF;
+ }
+ d += runetochar(d, &r);
+ }
+ *n = es2 - s;
+ memmove(s2, s, *n);
+ return d - s1;
+}
--- /dev/null
+++ b/emu/Solaris/asm-386.s
@@ -1,0 +1,72 @@
+ .section .bss
+ .align 4
+.L4_.bss:
+ .align 4
+Solaris_Asm_IntP: / Offset 0
+ .type Solaris_Asm_IntP,@object
+ .size Solaris_Asm_IntP,4
+ .set .,.+4
+Solaris_Asm_VoidP: / Offset 4
+ .type Solaris_Asm_VoidP,@object
+ .size Solaris_Asm_VoidP,4
+ .set .,.+4
+ .section .text
+ .align 4
+.L1_.text:
+
+/====================
+/ FPsave
+/--------------------
+ .align 4
+ .align 4
+ .globl FPsave
+FPsave:
+ pushl %ebp
+ movl %esp,%ebp
+ movl 8(%ebp),%eax
+ movl %eax,Solaris_Asm_VoidP
+ fstenv (%eax)
+ leave
+ ret
+ .align 4
+ .type FPsave,@function
+ .size FPsave,.-FPsave
+
+/====================
+/ FPrestore
+/--------------------
+ .align 4
+ .globl FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp,%ebp
+ movl 8(%ebp),%eax
+ movl %eax,Solaris_Asm_VoidP
+ fldenv (%eax)
+ leave
+ ret
+ .align 4
+ .type FPrestore,@function
+ .size FPrestore,.-FPrestore
+
+
+/====================
+/ getcallerpc
+/--------------------
+ .align 4
+ .globl getcallerpc
+getcallerpc:
+ movl 4(%ebp),%eax
+ ret
+ .align 4
+ .type getcallerpc,@function
+ .size getcallerpc,.-getcallerpc
+
+/ test-and-set
+ .align 4
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/Solaris/asm-sparc.s
@@ -1,0 +1,76 @@
+
+ .section ".text", #alloc, #execinstr
+ .align 8
+ .skip 16
+ .global segflush
+ .type segflush, #function
+
+ ! The flush instruction works on 8-byte chunks.
+ ! We truncate the pointer and increase the count
+ ! to make sure we flush the right range.
+
+ ! SPARC requires 5 instructions after flush to
+ ! let the caches settle. The loop code supplies
+ ! the delay instructions.
+
+segflush: ! int segflush(void *p, ulong len)
+
+ and %o0,-8,%o0 ! clear low 3 bits of p
+ add %o1, 7, %o1 ! len += 7
+1:
+ flush %o0 ! synchronize cache
+ sub %o1, 8, %o1 ! len -= 8
+ cmp %o1, 0 ! if len > 0, repeat
+ bg 1b
+ add %o0, 8, %o0 ! p += 8 in delay slot
+
+ retl
+ add %g0, %g0, %o0 ! return 0
+ .size segflush,(.-segflush)
+
+
+ .section ".text", #alloc, #execinstr
+ .align 8
+ .skip 16
+ .global FPsave
+ .type FPsave, #function
+FPsave:
+ retl
+ st %fsr,[%o0]
+ .size FPsave,(.-FPsave)
+
+
+ .section ".text", #alloc, #execinstr
+ .align 8
+ .skip 16
+ .global FPrestore
+ .type FPrestore, #function
+FPrestore:
+ retl
+ ld [%o0],%fsr
+ .size FPrestore,(.-FPrestore)
+
+
+ .section ".text", #alloc, #execinstr
+ .align 8
+ .skip 16
+ .global getcallerpc
+ .type getcallerpc, #function
+getcallerpc: ! ignore argument
+ retl
+ add %i7,0,%o0
+
+ .size getcallerpc,(.-getcallerpc)
+
+
+ .section ".text", #alloc, #execinstr
+ .align 8
+ .skip 16
+ .global _tas
+ .type _tas, #function
+_tas:
+ or %g0,1,%o1
+ swap [%o0],%o1
+ retl
+ or %g0,%o1,%o0
+ .size _tas,(.-_tas)
--- /dev/null
+++ b/emu/Solaris/audio.c
@@ -1,0 +1,593 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#define __EXTENSIONS__
+#include <sys/time.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stropts.h>
+#include <sys/audioio.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/audioio.h>
+
+#define Audio_Mic_Val AUDIO_MICROPHONE
+#define Audio_Linein_Val AUDIO_LINE_IN
+
+#define Audio_Speaker_Val AUDIO_SPEAKER
+#define Audio_Headphone_Val AUDIO_HEADPHONE
+#define Audio_Lineout_Val AUDIO_LINE_OUT
+
+#define Audio_Pcm_Val AUDIO_ENCODING_LINEAR
+#define Audio_Ulaw_Val AUDIO_ENCODING_ULAW
+#define Audio_Alaw_Val AUDIO_ENCODING_ALAW
+
+#include "audio-tbls.c"
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+static int debug = 0;
+
+extern int nanosleep(const struct timespec *, struct timespec *);
+
+#define AUDIO_FILE_STRING "/dev/audio"
+
+enum {
+ A_Pause,
+ A_UnPause
+};
+
+enum {
+ A_In,
+ A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static int audio_file_in = -1; /* file in */
+static int audio_file_out = -1; /* file out */
+
+static int audio_swap_flag = 0; /* endian swap */
+
+static int audio_in_pause = A_UnPause;
+
+static Audio_t av;
+
+static int audio_enforce(Audio_t*);
+static int audio_open_in(void);
+static int audio_open_out(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+ static ushort flag = 1;
+ audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */
+ audio_info_init(&av);
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ switch(omode){
+ case OREAD:
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in >= 0)
+ error(Einuse);
+ if((audio_file_in = audio_open_in()) < 0)
+ oserror();
+
+ poperror();
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_out >= 0)
+ error(Einuse);
+ if((audio_file_out = audio_open_out() ) < 0)
+ oserror();
+ poperror();
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&inlock);
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_in >= 0 || audio_file_out >= 0)
+ error(Einuse);
+
+ if((audio_file_in = audio_open_in()) < 0)
+ oserror();
+ if(waserror()){
+ close(audio_file_in);
+ audio_file_in = -1;
+ nexterror();
+ }
+ if((audio_file_out = audio_open_out()) < 0)
+ oserror();
+ poperror();
+
+ poperror();
+ qunlock(&inlock);
+ qunlock(&outlock);
+ break;
+ }
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&inlock);
+ close(audio_file_in);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ close(audio_file_out);
+ audio_file_out = -1;
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ close(audio_file_in);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ qlock(&outlock);
+ close(audio_file_out);
+ audio_file_out = -1;
+ qunlock(&outlock);
+ break;
+ }
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long ba, status, chunk, total;
+ char *pva = (char *) va;
+
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(!audio_pause_in(audio_file_in, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count) {
+ chunk = count - total;
+ osenter();
+ status = read(audio_file_in, pva + total, chunk);
+ osleave();
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(pva, count);
+
+ poperror();
+ qunlock(&inlock);
+
+ time.tv_sec = 0; /* hack around broken thread scheduler in Solaris */
+ time.tv_nsec= 1;
+ nanosleep(&time,nil);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(audio_file_out < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(va, count);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count) {
+ chunk = min(bufsz, count - total);
+ osenter();
+ status = write(audio_file_out, va, chunk);
+ osleave();
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ time.tv_sec = 0; /* hack around broken thread scheduler in Solaris */
+ time.tv_nsec= 1;
+ nanosleep(&time,nil);
+
+ return count;
+}
+
+int
+audio_open_in(void)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_RDONLY|O_NONBLOCK);
+
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ close(fd);
+ error(Eio);
+ }
+
+ if(!audio_pause_in(fd, A_Pause)) {
+ close(fd);
+ error(Eio);
+ }
+
+ if(!audio_flush(fd, A_In)) {
+ close(fd);
+ error(Eio);
+ }
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = 0;
+
+ if(!audio_set_info(fd, &av.in, A_In)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+int
+audio_open_out(void)
+{
+ int fd;
+ struct audio_info hdr;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_WRONLY|O_NONBLOCK);
+
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ /* set audio info */
+ av.in.flags = 0;
+ av.out.flags = ~0;
+
+ if(!audio_set_info(fd, &av.out, A_Out)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.out.flags = 0;
+
+ return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ int fd;
+ int ff;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if (!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if (!audio_enforce(&tmpav))
+ error(Ebadarg);
+
+ qlock(&inlock);
+ if (waserror()) {
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+ if (!audio_pause_in(audio_file_in, A_Pause))
+ error(Ebadarg);
+ if (!audio_flush(audio_file_in, A_In))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&inlock);
+
+ qlock(&outlock);
+ if (waserror()) {
+ qunlock(&outlock);
+ nexterror();
+ }
+ if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+ if (!audio_pause_out(audio_file_out))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&outlock);
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status;
+ int unequal_stereo = 0;
+ audio_info_t info;
+ audio_prinfo_t *dev;
+
+ if(fd < 0)
+ return 0;
+
+ /* devitialize header */
+ AUDIO_INITINFO(&info);
+
+ if(d == A_In)
+ dev = &info.record;
+ else
+ dev = &info.play;
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG)
+ dev->sample_rate = i->rate;
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG)
+ dev->channels = i->chan;
+
+ /* precision */
+ if(i->flags & AUDIO_BITS_FLAG)
+ dev->precision = i->bits;
+
+ /* encoding */
+ if(i->flags & AUDIO_ENC_FLAG)
+ dev->encoding = i->enc;
+
+ /* devices */
+ if(i->flags & AUDIO_DEV_FLAG)
+ dev->port = i->dev;
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG|AUDIO_VOL_FLAG)) {
+ dev->gain = (i->left * AUDIO_MAX_GAIN) / Audio_Max_Val;
+
+ /* do left first then right later */
+ if(i->left == i->right)
+ dev->balance = AUDIO_MID_BALANCE;
+ else {
+ dev->balance = AUDIO_LEFT_BALANCE;
+ if(i->chan != 1)
+ unequal_stereo = 1;
+ }
+ }
+
+ osenter();
+ status = ioctl(fd, AUDIO_SETINFO, &info); /* qlock and load general stuff */
+ osleave();
+
+ if(status == -1) {
+ if(debug) print("audio_set_info 1 failed: fd = %d errno = %d\n", fd, errno);
+ return 0;
+ }
+
+ /* check for different right and left for dev */
+ if(unequal_stereo) {
+
+ /* re-init header */
+ AUDIO_INITINFO(&info);
+
+ dev->gain = (i->right * AUDIO_MAX_GAIN) / Audio_Max_Val;
+ dev->balance == AUDIO_RIGHT_BALANCE;
+
+ osenter();
+ status = ioctl(fd, AUDIO_SETINFO, &info);
+ osleave();
+
+ if(status == -1) {
+ if(debug) print("audio_set_info 2 failed: fd = %d errno = %d\n",fd, errno);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+void
+audio_swap_endian(char *p, int n)
+{
+ int b;
+
+ while (n > 1) {
+ b = p[0];
+ p[0] = p[1];
+ p[1] = b;
+ p += 2;
+ n -= 2;
+ }
+}
+
+static int
+audio_pause_out(int fd)
+{
+ audio_info_t info;
+ int foo = 0;
+ int status;
+
+ osenter();
+ status = ioctl(fd, AUDIO_DRAIN, &foo);
+ osleave();
+
+ if(status == -1)
+ return 0;
+ return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+ audio_info_t info;
+ int status;
+
+ if(fd < 0)
+ return 0;
+
+ if(audio_in_pause == f)
+ return 1;
+
+ /* initialize header */
+ AUDIO_INITINFO(&info);
+
+ /* unpause input */
+ if(f == A_Pause)
+ info.record.pause = 1;
+ else
+ info.record.pause = 0;
+
+ osenter();
+ status = ioctl(fd, AUDIO_SETINFO, &info);
+ osleave();
+
+ if(status == -1)
+ return 0;
+
+ audio_in_pause = f;
+
+ return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+ int flag = d==A_In? FLUSHR: FLUSHW;
+ int status;
+
+ osenter();
+ status = ioctl(fd, I_FLUSH, flag); /* drain anything already put into buffer */
+ osleave();
+
+ if(status == -1)
+ return 0;
+ return 1;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+ if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) &&
+ (t->in.rate != 8000 || t->in.chan != 1))
+ return 0;
+ if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) &&
+ (t->out.rate != 8000 || t->out.chan != 1))
+ return 0;
+ return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
--- /dev/null
+++ b/emu/Solaris/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgrp();
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/Solaris/deveia.c
@@ -1,0 +1,38 @@
+/*
+ * Solaris serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/term/a",
+ "/dev/term/b"
+};
+
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {76800, B76800},
+ {115200, B115200},
+ {153600, B153600},
+ {230400, B230400},
+ {307200, B307200},
+ {460800, B460800},
+ {-1, -1}
+};
--- /dev/null
+++ b/emu/Solaris/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/Solaris/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+ audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Solaris/mkfile
@@ -1,0 +1,46 @@
+SYSTARG=Solaris
+OBJTYPE=sparc
+<../../mkconfig
+SYSTARG=Solaris
+OBJTYPE=sparc
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS=$EMULIBS
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O: ../port/devfs-posix.c
--- /dev/null
+++ b/emu/Solaris/os.c
@@ -1,0 +1,436 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#undef _POSIX_C_SOURCE
+#undef getwd
+#include <unistd.h>
+#include <thread.h>
+#include <time.h>
+#include <termios.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+enum
+{
+ DELETE = 0x7F
+};
+char *hosttype = "Solaris";
+
+static thread_key_t prdakey;
+
+static siginfo_t siginfo;
+
+extern int dflag;
+
+Proc*
+getup(void)
+{
+ void *vp;
+
+ if (thr_getspecific(prdakey, &vp))
+ return nil;
+ return vp;
+}
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+
+ lock(&procs.l);
+ if(up->prev)
+ up->prev->next = up->next;
+ else
+ procs.head = up->next;
+
+ if(up->next)
+ up->next->prev = up->prev;
+ else
+ procs.tail = up->prev;
+ unlock(&procs.l);
+
+ /*print("pexit: %s: %s\n", up->text, msg);*/
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(up->prog);
+ sema_destroy(up->os);
+ free(up->os);
+ free(up);
+ thr_exit(0);
+}
+
+static void *
+tramp(void *v)
+{
+ struct Proc *Up;
+
+ if(thr_setspecific(prdakey, v)) {
+ print("set specific data failed in tramp\n");
+ thr_exit(0);
+ }
+ Up = v;
+ Up->sigid = thr_self();
+ Up->func(Up->arg);
+ pexit("", 0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ thread_t thread;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ sema_t *sem;
+
+ p = newproc();
+
+ sem = malloc(sizeof(*sem));
+ if(sem == nil)
+ panic("can't allocate semaphore");
+ sema_init(sem, 0, USYNC_THREAD, 0);
+ p->os = sem;
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if(thr_create(0, 0, &tramp, p, THR_BOUND|THR_DETACHED, &thread))
+ panic("thr_create failed\n");
+ thr_yield();
+}
+
+/* to get pc on trap use siginfo.si_pc field and define all trap handlers
+ as printILL - have to set sa_sigaction, sa_flags not sa_handler
+*/
+
+static void
+trapUSR1(void)
+{
+ int intwait;
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+ if(intwait == 0) /* Not posted so its a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+static void
+trapILL(void)
+{
+ disfault(nil, "Illegal instruction");
+}
+
+static void
+printILL(int sig, siginfo_t *siginfo, void *v)
+{
+ panic("Illegal instruction with code=%d at address=%x, opcode=%x.\n",
+ siginfo->si_code, siginfo->si_addr,*(char*)siginfo->si_addr);
+}
+
+static void
+trapBUS(void)
+{
+ disfault(nil, "Bus error");
+}
+
+static void
+trapSEGV(void)
+{
+ disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(void)
+{
+ disfault(nil, "Floating point exception");
+}
+
+void
+oshostintr(Proc *p)
+{
+ thr_kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ while(sema_wait(up->os))
+ ; /* retry on signals */
+}
+
+void
+osready(Proc *p)
+{
+ sema_post(p->os);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+static struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+ exit(0);
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody(void)
+{
+ struct passwd *pwd;
+
+ if (pwd=getpwnam("nobody")) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+ struct Proc *Up;
+ struct sigaction act;
+ struct passwd *pw;
+ char sys[64];
+
+ setsid();
+
+ if(dflag == 0)
+ termset();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ getnobody();
+
+ memset(&act, 0 , sizeof(act));
+ act.sa_handler=trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+ /*
+ * For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGPIPE, SIG_IGN);
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+ if(sflag == 0) {
+ act.sa_handler = trapBUS;
+ sigaction(SIGBUS, &act, nil);
+ act.sa_handler = trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_handler = trapSEGV;
+ sigaction(SIGSEGV, &act, nil);
+ act.sa_handler = trapFPE;
+ sigaction(SIGFPE, &act, nil);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+ } else{
+ act.sa_sigaction = printILL;
+ act.sa_flags=SA_SIGINFO;
+ sigaction(SIGILL, &act, nil);
+ }
+
+ if(thr_keycreate(&prdakey,NULL))
+ print("keycreate failed\n");
+
+ Up = newproc();
+ if(thr_setspecific(prdakey,Up))
+ panic("set specific thread data failed\n");
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ up->env->uid = getuid();
+ up->env->gid = getgid();
+ emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ fprint(2, "keyboard read: %s\n", strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t, NULL)<0)
+ return(0);
+ if(sec0==0){
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec/1000;
+ time.tv_nsec= (milsec%1000)*1000000;
+ nanosleep(&time,nil);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ thr_yield();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
--- /dev/null
+++ b/emu/Unixware/asm-386.s
@@ -1,0 +1,71 @@
+ .section .bss
+ .align 4
+.L4_.bss:
+ .align 4
+Unixware_Asm_IntP: / Offset 0
+ .type Unixware_Asm_IntP,@object
+ .size Unixware_Asm_IntP,4
+ .set .,.+4
+Unixware_Asm_VoidP: / Offset 4
+ .type Unixware_Asm_VoidP,@object
+ .size Unixware_Asm_VoidP,4
+ .set .,.+4
+ .section .text
+ .align 4
+.L1_.text:
+/====================
+/ FPsave
+/--------------------
+ .align 4
+ .align 4
+ .globl FPsave
+FPsave:
+ pushl %ebp
+ movl %esp,%ebp
+ movl 8(%ebp),%eax
+ movl %eax,Unixware_Asm_VoidP
+ fstenv (%eax)
+ leave
+ ret
+ .align 4
+ .type FPsave,@function
+ .size FPsave,.-FPsave
+
+/====================
+/ FPrestore
+/--------------------
+ .align 4
+ .globl FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp,%ebp
+ movl 8(%ebp),%eax
+ movl %eax,Unixware_Asm_VoidP
+ fldenv (%eax)
+ leave
+ ret
+ .align 4
+ .type FPrestore,@function
+ .size FPrestore,.-FPrestore
+
+
+/====================
+/ getcallerpc
+/--------------------
+ .align 4
+ .globl getcallerpc
+getcallerpc:
+ movl 4(%ebp),%eax
+ ret
+ .align 4
+ .type getcallerpc,@function
+ .size getcallerpc,.-getcallerpc
+
+/ test-and-set
+ .align 4
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
--- /dev/null
+++ b/emu/Unixware/cmd.c
@@ -1,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgrp();
+ if(nice)
+ oslopri();
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
--- /dev/null
+++ b/emu/Unixware/deveia.c
@@ -1,0 +1,11 @@
+/*
+ * Solaris serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/cua/a",
+ "/dev/cua/b"
+};
+
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
--- /dev/null
+++ b/emu/Unixware/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+ return 0;
+}
--- /dev/null
+++ b/emu/Unixware/emu
@@ -1,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw win-x11a
+ pointer
+ snarf
+
+ ip ipif-posix ipaux
+ eia
+# audio audio
+ mem
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
--- /dev/null
+++ b/emu/Unixware/mkfile
@@ -1,0 +1,44 @@
+SYSTARG=Unixware
+OBJTYPE=386
+<../../mkconfig
+SYSTARG=Unixware
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ os.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lm -lX11 -lsocket -lnsl -lresolv -lxti -Kthread
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
--- /dev/null
+++ b/emu/Unixware/mkfile-Unixware
@@ -1,0 +1,15 @@
+#
+# architecture-dependent files for Unixware
+#
+
+TARGFILES=asm-Unixware-$OBJTYPE.$O\
+ devfs-posix.$O\
+ devip.$O\
+ ipif-posix.$O\
+ os-Unixware.$O\
+ win-x11.$O\
+ deveia-Unixware.$O\
+ srv.$O\
+ lock.$O\
+
+SYSLIBS= -lm -lX11 -lsocket -lnsl -lresolv -lxti -Kthread
--- /dev/null
+++ b/emu/Unixware/os.c
@@ -1,0 +1,437 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#undef _POSIX_C_SOURCE
+#undef getwd
+#include <unistd.h>
+#include <thread.h>
+#include <time.h>
+#include <termios.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+enum
+{
+ DELETE = 0x7F
+};
+char *hosttype = "Unixware";
+
+static thread_key_t prdakey;
+
+static siginfo_t siginfo;
+
+extern int dflag;
+
+Proc*
+getup(void)
+{
+ void *vp;
+
+ if (thr_getspecific(prdakey, &vp))
+ return nil;
+ return vp;
+}
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+
+ lock(&procs.l);
+ if(up->prev)
+ up->prev->next = up->next;
+ else
+ procs.head = up->next;
+
+ if(up->next)
+ up->next->prev = up->prev;
+ else
+ procs.tail = up->prev;
+ unlock(&procs.l);
+
+ /*print("pexit: %s: %s\n", up->text, msg);*/
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(up->prog);
+ sema_destroy(up->os);
+ free(up->os);
+ free(up);
+ thr_exit(0);
+}
+
+static void *
+tramp(void *v)
+{
+ struct Proc *Up;
+
+ if(thr_setspecific(prdakey, v)) {
+ print("set specific data failed in tramp\n");
+ thr_exit(0);
+ }
+ Up = v;
+ Up->sigid = thr_self();
+ Up->func(Up->arg);
+ pexit("", 0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ thread_t thread;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ sema_t *sem;
+
+ p = newproc();
+
+ sem = malloc(sizeof(*sem));
+ if(sem == nil)
+ panic("can't allocate semaphore");
+ sema_init(sem, 0, USYNC_THREAD, 0);
+ p->os = sem;
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ if(thr_create(0, 0, &tramp, p, THR_BOUND|THR_DETACHED, &thread))
+ panic("thr_create failed\n");
+ thr_yield();
+}
+
+/* to get pc on trap use siginfo.si_pc field and define all trap handlers
+ as printILL - have to set sa_sigaction, sa_flags not sa_handler
+*/
+
+static void
+trapUSR1(void)
+{
+ int intwait;
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+
+ if(intwait == 0) /* Not posted so it's a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+static void
+trapILL(void)
+{
+ disfault(nil, "Illegal instruction");
+}
+
+static void
+printILL(int sig, siginfo_t *siginfo, void *v)
+{
+ panic("Illegal instruction with code=%d at address=%x, opcode=%x.\n",
+ siginfo->si_code, siginfo->si_addr,*(char*)siginfo->si_addr);
+}
+
+static void
+trapBUS(void)
+{
+ disfault(nil, "Bus error");
+}
+
+static void
+trapSEGV(void)
+{
+ disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(void)
+{
+ disfault(nil, "Floating point exception");
+}
+
+void
+oshostintr(Proc *p)
+{
+ thr_kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ while(sema_wait(up->os))
+ ; /* retry on signals */
+}
+
+void
+osready(Proc *p)
+{
+ sema_post(p->os);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+ siglongjmp(env, val);
+}
+
+static struct termios tinit;
+
+static void
+termset(void)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+ tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+ exit(0);
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody(void)
+{
+ struct passwd *pwd;
+
+ if (pwd=getpwnam("nobody")) {
+ uidnobody = pwd->pw_uid;
+ gidnobody = pwd->pw_gid;
+ }
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ panic("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+ struct Proc *Up;
+ struct sigaction act;
+ struct passwd *pw;
+ char sys[64];
+
+ setsid();
+
+ if(dflag == 0)
+ termset();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ getnobody();
+
+ memset(&act, 0 , sizeof(act));
+ act.sa_handler=trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+ /*
+ * For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGPIPE, SIG_IGN);
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+ if(sflag == 0) {
+ act.sa_handler = trapBUS;
+ sigaction(SIGBUS, &act, nil);
+ act.sa_handler = trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_handler = trapSEGV;
+ sigaction(SIGSEGV, &act, nil);
+ act.sa_handler = trapFPE;
+ sigaction(SIGFPE, &act, nil);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+ } else{
+ act.sa_sigaction = printILL;
+ act.sa_flags=SA_SIGINFO;
+ sigaction(SIGILL, &act, nil);
+ }
+
+ if(thr_keycreate(&prdakey,NULL))
+ print("keycreate failed\n");
+
+ Up = newproc();
+ if(thr_setspecific(prdakey,Up))
+ panic("set specific thread data failed\n");
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ up->env->uid = getuid();
+ up->env->gid = getgid();
+ emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ fprint(2, "keyboard read: %s\n", strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t, NULL)<0)
+ return(0);
+ if(sec0==0){
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000);
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec/1000;
+ time.tv_nsec= (milsec%1000)*1000000;
+ nanosleep(&time,nil);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ thr_yield();
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ pause();
+}
+
+void
+oslopri(void)
+{
+ setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
--- /dev/null
+++ b/emu/mkfile
@@ -1,0 +1,19 @@
+<../mkconfig
+
+all:V: all-$HOSTMODEL
+install:V: install-$HOSTMODEL
+safeinstall:V: safeinstall-$HOSTMODEL
+clean:V: clean-$HOSTMODEL
+nuke:V: nuke-$HOSTMODEL
+
+&-Posix:QV:
+ echo "(cd $SYSTARG; mk $MKFLAGS $stem)"
+ (cd $SYSTARG; mk $MKFLAGS $stem) || exit 1
+
+&-Nt:QV:
+ echo '@{builtin cd' $SYSTARG '; mk $MKFLAGS $stem}'
+ @{builtin cd $SYSTARG; mk $MKFLAGS $stem }
+
+&-Plan9:QV:
+ echo '@{builtin cd' $SYSTARG '; mk $MKFLAGS $stem}'
+ @{builtin cd $SYSTARG; mk $MKFLAGS $stem }
--- /dev/null
+++ b/emu/port/acme-offset
@@ -1,0 +1,1 @@
+X ,x/b?(read|write)\(.*ulong.*\)$/v/(bread|bwrite)/x/ulong offset/c/vlong offset
--- /dev/null
+++ b/emu/port/alloc.c
@@ -1,0 +1,1021 @@
+#include "dat.h"
+#include "fns.h"
+#include "interp.h"
+#include "error.h"
+
+enum
+{
+ MAXPOOL = 4
+};
+
+#define left u.s.bhl
+#define right u.s.bhr
+#define fwd u.s.bhf
+#define prev u.s.bhv
+#define parent u.s.bhp
+
+#define RESERVED 512*1024
+
+struct Pool
+{
+ char* name;
+ int pnum;
+ ulong maxsize;
+ int quanta;
+ int chunk;
+ int monitor;
+ ulong ressize; /* restricted size */
+ ulong cursize;
+ ulong arenasize;
+ ulong hw;
+ Lock l;
+ Bhdr* root;
+ Bhdr* chain;
+ ulong nalloc;
+ ulong nfree;
+ int nbrk;
+ int lastfree;
+ void (*move)(void*, void*);
+};
+
+void* initbrk(ulong);
+
+struct
+{
+ int n;
+ Pool pool[MAXPOOL];
+ /* Lock l; */
+} table = {
+ 3,
+ {
+ { "main", 0, 32*1024*1024, 31, 512*1024, 0, 31*1024*1024 },
+ { "heap", 1, 32*1024*1024, 31, 512*1024, 0, 31*1024*1024 },
+ { "image", 2, 64*1024*1024+256, 31, 4*1024*1024, 1, 63*1024*1024 },
+ }
+};
+
+Pool* mainmem = &table.pool[0];
+Pool* heapmem = &table.pool[1];
+Pool* imagmem = &table.pool[2];
+
+static void _auditmemloc(char *, void *);
+void (*auditmemloc)(char *, void *) = _auditmemloc;
+static void _poolfault(void *, char *, ulong);
+void (*poolfault)(void *, char *, ulong) = _poolfault;
+
+/* non tracing
+ *
+enum {
+ Npadlong = 0,
+ MallocOffset = 0,
+ ReallocOffset = 0
+};
+ *
+ */
+
+/* tracing */
+enum {
+ Npadlong = 2,
+ MallocOffset = 0,
+ ReallocOffset = 1
+};
+
+enum {
+ Monitor = 1
+};
+
+void (*memmonitor)(int, ulong, ulong, ulong) = nil;
+#define MM(v,pc,base,size) if(!Monitor || memmonitor==nil){} else memmonitor((v),(pc),(base),(size))
+
+#define CKLEAK 0
+int ckleak;
+#define ML(v, sz, pc) if(CKLEAK && ckleak && v){ if(sz) fprint(2, "%lux %lux %lux\n", (ulong)v, (ulong)sz, (ulong)pc); else fprint(2, "%lux\n", (ulong)v); }
+
+int
+memusehigh(void)
+{
+ return mainmem->cursize > mainmem->ressize ||
+ heapmem->cursize > heapmem->ressize ||
+ 0 && imagmem->cursize > imagmem->ressize;
+}
+
+int
+memlow(void)
+{
+ return heapmem->cursize > (heapmem->maxsize)/2;
+}
+
+int
+poolsetsize(char *s, int size)
+{
+ int i;
+
+ for(i = 0; i < table.n; i++) {
+ if(strcmp(table.pool[i].name, s) == 0) {
+ table.pool[i].maxsize = size;
+ table.pool[i].ressize = size-RESERVED;
+ if(size < RESERVED)
+ panic("not enough memory");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void
+poolimmutable(void *v)
+{
+ Bhdr *b;
+
+ D2B(b, v);
+ b->magic = MAGIC_I;
+}
+
+void
+poolmutable(void *v)
+{
+ Bhdr *b;
+
+ D2B(b, v);
+ b->magic = MAGIC_A;
+ ((Heap*)v)->color = mutator;
+}
+
+char*
+poolname(Pool *p)
+{
+ return p->name;
+}
+
+Bhdr*
+poolchain(Pool *p)
+{
+ return p->chain;
+}
+
+void
+pooldel(Pool *p, Bhdr *t)
+{
+ Bhdr *s, *f, *rp, *q;
+
+ if(t->parent == nil && p->root != t) {
+ t->prev->fwd = t->fwd;
+ t->fwd->prev = t->prev;
+ return;
+ }
+
+ if(t->fwd != t) {
+ f = t->fwd;
+ s = t->parent;
+ f->parent = s;
+ if(s == nil)
+ p->root = f;
+ else {
+ if(s->left == t)
+ s->left = f;
+ else
+ s->right = f;
+ }
+
+ rp = t->left;
+ f->left = rp;
+ if(rp != nil)
+ rp->parent = f;
+ rp = t->right;
+ f->right = rp;
+ if(rp != nil)
+ rp->parent = f;
+
+ t->prev->fwd = t->fwd;
+ t->fwd->prev = t->prev;
+ return;
+ }
+
+ if(t->left == nil)
+ rp = t->right;
+ else {
+ if(t->right == nil)
+ rp = t->left;
+ else {
+ f = t;
+ rp = t->right;
+ s = rp->left;
+ while(s != nil) {
+ f = rp;
+ rp = s;
+ s = rp->left;
+ }
+ if(f != t) {
+ s = rp->right;
+ f->left = s;
+ if(s != nil)
+ s->parent = f;
+ s = t->right;
+ rp->right = s;
+ if(s != nil)
+ s->parent = rp;
+ }
+ s = t->left;
+ rp->left = s;
+ s->parent = rp;
+ }
+ }
+ q = t->parent;
+ if(q == nil)
+ p->root = rp;
+ else {
+ if(t == q->left)
+ q->left = rp;
+ else
+ q->right = rp;
+ }
+ if(rp != nil)
+ rp->parent = q;
+}
+
+void
+pooladd(Pool *p, Bhdr *q)
+{
+ int size;
+ Bhdr *tp, *t;
+
+ q->magic = MAGIC_F;
+
+ q->left = nil;
+ q->right = nil;
+ q->parent = nil;
+ q->fwd = q;
+ q->prev = q;
+
+ t = p->root;
+ if(t == nil) {
+ p->root = q;
+ return;
+ }
+
+ size = q->size;
+
+ tp = nil;
+ while(t != nil) {
+ if(size == t->size) {
+ q->prev = t->prev;
+ q->prev->fwd = q;
+ q->fwd = t;
+ t->prev = q;
+ return;
+ }
+ tp = t;
+ if(size < t->size)
+ t = t->left;
+ else
+ t = t->right;
+ }
+
+ q->parent = tp;
+ if(size < tp->size)
+ tp->left = q;
+ else
+ tp->right = q;
+}
+
+static void*
+dopoolalloc(Pool *p, ulong asize, ulong pc)
+{
+ Bhdr *q, *t;
+ int alloc, ldr, ns, frag;
+ int osize, size;
+
+ if(asize >= 1024*1024*1024) /* for sanity and to avoid overflow */
+ return nil;
+ size = asize;
+ osize = size;
+ size = (size + BHDRSIZE + p->quanta) & ~(p->quanta);
+
+ lock(&p->l);
+ p->nalloc++;
+
+ t = p->root;
+ q = nil;
+ while(t) {
+ if(t->size == size) {
+ t = t->fwd;
+ pooldel(p, t);
+ t->magic = MAGIC_A;
+ p->cursize += t->size;
+ if(p->cursize > p->hw)
+ p->hw = p->cursize;
+ unlock(&p->l);
+ if(p->monitor)
+ MM(p->pnum, pc, (ulong)B2D(t), size);
+ return B2D(t);
+ }
+ if(size < t->size) {
+ q = t;
+ t = t->left;
+ }
+ else
+ t = t->right;
+ }
+ if(q != nil) {
+ pooldel(p, q);
+ q->magic = MAGIC_A;
+ frag = q->size - size;
+ if(frag < (size>>2) && frag < 0x8000) {
+ p->cursize += q->size;
+ if(p->cursize > p->hw)
+ p->hw = p->cursize;
+ unlock(&p->l);
+ if(p->monitor)
+ MM(p->pnum, pc, (ulong)B2D(q), size);
+ return B2D(q);
+ }
+ /* Split */
+ ns = q->size - size;
+ q->size = size;
+ B2T(q)->hdr = q;
+ t = B2NB(q);
+ t->size = ns;
+ B2T(t)->hdr = t;
+ pooladd(p, t);
+ p->cursize += q->size;
+ if(p->cursize > p->hw)
+ p->hw = p->cursize;
+ unlock(&p->l);
+ if(p->monitor)
+ MM(p->pnum, pc, (ulong)B2D(q), size);
+ return B2D(q);
+ }
+
+ ns = p->chunk;
+ if(size > ns)
+ ns = size;
+ ldr = p->quanta+1;
+
+ alloc = ns+ldr+ldr;
+ p->arenasize += alloc;
+ if(p->arenasize > p->maxsize) {
+ p->arenasize -= alloc;
+ ns = p->maxsize-p->arenasize-ldr-ldr;
+ ns &= ~p->quanta;
+ if (ns < size) {
+ if(poolcompact(p)) {
+ unlock(&p->l);
+ return poolalloc(p, osize);
+ }
+
+ unlock(&p->l);
+ print("arena %s too large: size %d cursize %lud arenasize %lud maxsize %lud\n",
+ p->name, size, p->cursize, p->arenasize, p->maxsize);
+ return nil;
+ }
+ alloc = ns+ldr+ldr;
+ p->arenasize += alloc;
+ }
+
+ p->nbrk++;
+ t = (Bhdr *)sbrk(alloc);
+ if(t == (void*)-1) {
+ p->nbrk--;
+ unlock(&p->l);
+ return nil;
+ }
+ /* Double alignment */
+ t = (Bhdr *)(((ulong)t + 7) & ~7);
+
+ if(p->chain != nil && (char*)t-(char*)B2LIMIT(p->chain)-ldr == 0){
+ /* can merge chains */
+ if(0)print("merging chains %p and %p in %s\n", p->chain, t, p->name);
+ q = B2LIMIT(p->chain);
+ q->magic = MAGIC_A;
+ q->size = alloc;
+ B2T(q)->hdr = q;
+ t = B2NB(q);
+ t->magic = MAGIC_E;
+ p->chain->csize += alloc;
+ p->cursize += alloc;
+ unlock(&p->l);
+ poolfree(p, B2D(q)); /* for backward merge */
+ return poolalloc(p, osize);
+ }
+
+ t->magic = MAGIC_E; /* Make a leader */
+ t->size = ldr;
+ t->csize = ns+ldr;
+ t->clink = p->chain;
+ p->chain = t;
+ B2T(t)->hdr = t;
+ t = B2NB(t);
+
+ t->magic = MAGIC_A; /* Make the block we are going to return */
+ t->size = size;
+ B2T(t)->hdr = t;
+ q = t;
+
+ ns -= size; /* Free the rest */
+ if(ns > 0) {
+ q = B2NB(t);
+ q->size = ns;
+ B2T(q)->hdr = q;
+ pooladd(p, q);
+ }
+ B2NB(q)->magic = MAGIC_E; /* Mark the end of the chunk */
+
+ p->cursize += t->size;
+ if(p->cursize > p->hw)
+ p->hw = p->cursize;
+ unlock(&p->l);
+ if(p->monitor)
+ MM(p->pnum, pc, (ulong)B2D(t), size);
+ return B2D(t);
+}
+
+void *
+poolalloc(Pool *p, ulong asize)
+{
+ Prog *prog;
+
+ if(p->cursize > p->ressize && (prog = currun()) != nil && prog->flags&Prestricted)
+ return nil;
+ return dopoolalloc(p, asize, getcallerpc(&p));
+}
+
+void
+poolfree(Pool *p, void *v)
+{
+ Bhdr *b, *c;
+ extern Bhdr *ptr;
+
+ D2B(b, v);
+ if(p->monitor)
+ MM(p->pnum|(1<<8), getcallerpc(&p), (ulong)v, b->size);
+
+ lock(&p->l);
+ p->nfree++;
+ p->cursize -= b->size;
+ c = B2NB(b);
+ if(c->magic == MAGIC_F) { /* Join forward */
+ if(c == ptr)
+ ptr = b;
+ pooldel(p, c);
+ c->magic = 0;
+ b->size += c->size;
+ B2T(b)->hdr = b;
+ }
+
+ c = B2PT(b)->hdr;
+ if(c->magic == MAGIC_F) { /* Join backward */
+ if(b == ptr)
+ ptr = c;
+ pooldel(p, c);
+ b->magic = 0;
+ c->size += b->size;
+ b = c;
+ B2T(b)->hdr = b;
+ }
+ pooladd(p, b);
+ unlock(&p->l);
+}
+
+void *
+poolrealloc(Pool *p, void *v, ulong size)
+{
+ Bhdr *b;
+ void *nv;
+ int osize;
+
+ if(size >= 1024*1024*1024) /* for sanity and to avoid overflow */
+ return nil;
+ if(size == 0){
+ poolfree(p, v);
+ return nil;
+ }
+ SET(osize);
+ if(v != nil){
+ lock(&p->l);
+ D2B(b, v);
+ osize = b->size - BHDRSIZE;
+ unlock(&p->l);
+ if(osize >= size)
+ return v;
+ }
+ nv = poolalloc(p, size);
+ if(nv != nil && v != nil){
+ memmove(nv, v, osize);
+ poolfree(p, v);
+ }
+ return nv;
+}
+
+ulong
+poolmsize(Pool *p, void *v)
+{
+ Bhdr *b;
+ ulong size;
+
+ if(v == nil)
+ return 0;
+ lock(&p->l);
+ D2B(b, v);
+ size = b->size - BHDRSIZE;
+ unlock(&p->l);
+ return size;
+}
+
+static ulong
+poolmax(Pool *p)
+{
+ Bhdr *t;
+ ulong size;
+
+ lock(&p->l);
+ size = p->maxsize - p->cursize;
+ t = p->root;
+ if(t != nil) {
+ while(t->right != nil)
+ t = t->right;
+ if(size < t->size)
+ size = t->size;
+ }
+ if(size >= BHDRSIZE)
+ size -= BHDRSIZE;
+ unlock(&p->l);
+ return size;
+}
+
+ulong
+poolmaxsize(void)
+{
+ int i;
+ ulong total;
+
+ total = 0;
+ for(i = 0; i < nelem(table.pool); i++)
+ total += table.pool[i].maxsize;
+ return total;
+}
+
+int
+poolread(char *va, int count, ulong offset)
+{
+ Pool *p;
+ int n, i, signed_off;
+
+ n = 0;
+ signed_off = offset;
+ for(i = 0; i < table.n; i++) {
+ p = &table.pool[i];
+ n += snprint(va+n, count-n, "%11lud %11lud %11lud %11lud %11lud %11d %11lud %s\n",
+ p->cursize,
+ p->maxsize,
+ p->hw,
+ p->nalloc,
+ p->nfree,
+ p->nbrk,
+ poolmax(p),
+ p->name);
+
+ if(signed_off > 0) {
+ signed_off -= n;
+ if(signed_off < 0) {
+ memmove(va, va+n+signed_off, -signed_off);
+ n = -signed_off;
+ }
+ else
+ n = 0;
+ }
+
+ }
+ return n;
+}
+
+void*
+smalloc(size_t size)
+{
+ void *v;
+
+ for(;;){
+ v = malloc(size);
+ if(v != nil)
+ break;
+ if(0)
+ print("smalloc waiting from %lux\n", getcallerpc(&size));
+ osenter();
+ osmillisleep(100);
+ osleave();
+ }
+ setmalloctag(v, getcallerpc(&size));
+ setrealloctag(v, 0);
+ return v;
+}
+
+void*
+kmalloc(size_t size)
+{
+ void *v;
+
+ v = dopoolalloc(mainmem, size+Npadlong*sizeof(ulong), getcallerpc(&size));
+ if(v != nil){
+ ML(v, size, getcallerpc(&size));
+ if(Npadlong){
+ v = (ulong*)v+Npadlong;
+ setmalloctag(v, getcallerpc(&size));
+ setrealloctag(v, 0);
+ }
+ memset(v, 0, size);
+ MM(0, getcallerpc(&size), (ulong)v, size);
+ }
+ return v;
+}
+
+
+
+void*
+malloc(size_t size)
+{
+ void *v;
+
+ v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
+ if(v != nil){
+ ML(v, size, getcallerpc(&size));
+ if(Npadlong){
+ v = (ulong*)v+Npadlong;
+ setmalloctag(v, getcallerpc(&size));
+ setrealloctag(v, 0);
+ }
+ memset(v, 0, size);
+ MM(0, getcallerpc(&size), (ulong)v, size);
+ } else
+ print("malloc failed from %lux\n", getcallerpc(&size));
+ return v;
+}
+
+void*
+mallocz(ulong size, int clr)
+{
+ void *v;
+
+ v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
+ if(v != nil){
+ ML(v, size, getcallerpc(&size));
+ if(Npadlong){
+ v = (ulong*)v+Npadlong;
+ setmalloctag(v, getcallerpc(&size));
+ setrealloctag(v, 0);
+ }
+ if(clr)
+ memset(v, 0, size);
+ MM(0, getcallerpc(&size), (ulong)v, size);
+ } else
+ print("mallocz failed from %lux\n", getcallerpc(&size));
+ return v;
+}
+
+void
+free(void *v)
+{
+ Bhdr *b;
+
+ if(v != nil) {
+ if(Npadlong)
+ v = (ulong*)v-Npadlong;
+ D2B(b, v);
+ ML(v, 0, 0);
+ MM(1<<8|0, getcallerpc(&v), (ulong)((ulong*)v+Npadlong), b->size);
+ poolfree(mainmem, v);
+ }
+}
+
+void*
+realloc(void *v, size_t size)
+{
+ void *nv;
+
+ if(size == 0)
+ return malloc(size); /* temporary change until realloc calls can be checked */
+ if(v != nil)
+ v = (ulong*)v-Npadlong;
+ if(Npadlong!=0 && size!=0)
+ size += Npadlong*sizeof(ulong);
+ nv = poolrealloc(mainmem, v, size);
+ ML(v, 0, 0);
+ ML(nv, size, getcallerpc(&v));
+ if(nv != nil) {
+ nv = (ulong*)nv+Npadlong;
+ setrealloctag(nv, getcallerpc(&v));
+ if(v == nil)
+ setmalloctag(v, getcallerpc(&v));
+ } else
+ print("realloc failed from %lux\n", getcallerpc(&v));
+ return nv;
+}
+
+void
+setmalloctag(void *v, ulong pc)
+{
+ ulong *u;
+
+ USED(v);
+ USED(pc);
+ if(Npadlong <= MallocOffset || v == nil)
+ return;
+ u = v;
+ u[-Npadlong+MallocOffset] = pc;
+}
+
+ulong
+getmalloctag(void *v)
+{
+ USED(v);
+ if(Npadlong <= MallocOffset)
+ return ~0;
+ return ((ulong*)v)[-Npadlong+MallocOffset];
+}
+
+void
+setrealloctag(void *v, ulong pc)
+{
+ ulong *u;
+
+ USED(v);
+ USED(pc);
+ if(Npadlong <= ReallocOffset || v == nil)
+ return;
+ u = v;
+ u[-Npadlong+ReallocOffset] = pc;
+}
+
+ulong
+getrealloctag(void *v)
+{
+ USED(v);
+ if(Npadlong <= ReallocOffset)
+ return ((ulong*)v)[-Npadlong+ReallocOffset];
+ return ~0;
+}
+
+ulong
+msize(void *v)
+{
+ if(v == nil)
+ return 0;
+ return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
+}
+
+void*
+calloc(size_t n, size_t szelem)
+{
+ return malloc(n*szelem);
+}
+
+/*
+void
+pooldump(Pool *p)
+{
+ Bhdr *b, *base, *limit, *ptr;
+
+ b = p->chain;
+ if(b == nil)
+ return;
+ base = b;
+ ptr = b;
+ limit = B2LIMIT(b);
+
+ while(base != nil) {
+ print("\tbase #%.8lux ptr #%.8lux", base, ptr);
+ if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I)
+ print("\tA%.5d\n", ptr->size);
+ else if(ptr->magic == MAGIC_E)
+ print("\tE\tL#%.8lux\tS#%.8lux\n", ptr->clink, ptr->csize);
+ else
+ print("\tF%.5d\tL#%.8lux\tR#%.8lux\tF#%.8lux\tP#%.8lux\tT#%.8lux\n",
+ ptr->size, ptr->left, ptr->right, ptr->fwd, ptr->prev, ptr->parent);
+ ptr = B2NB(ptr);
+ if(ptr >= limit) {
+ print("link to #%.8lux\n", base->clink);
+ base = base->clink;
+ if(base == nil)
+ break;
+ ptr = base;
+ limit = B2LIMIT(base);
+ }
+ }
+}
+*/
+
+void
+poolsetcompact(Pool *p, void (*move)(void*, void*))
+{
+ p->move = move;
+}
+
+int
+poolcompact(Pool *pool)
+{
+ Bhdr *base, *limit, *ptr, *end, *next;
+ int compacted, nb;
+
+ if(pool->move == nil || pool->lastfree == pool->nfree)
+ return 0;
+
+ pool->lastfree = pool->nfree;
+
+ base = pool->chain;
+ ptr = B2NB(base); /* First Block in arena has clink */
+ limit = B2LIMIT(base);
+ compacted = 0;
+
+ pool->root = nil;
+ end = ptr;
+ while(base != nil) {
+ next = B2NB(ptr);
+ if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I) {
+ if(ptr != end) {
+ memmove(end, ptr, ptr->size);
+ pool->move(B2D(ptr), B2D(end));
+ compacted = 1;
+ }
+ end = B2NB(end);
+ }
+ if(next >= limit) {
+ nb = (uchar*)limit - (uchar*)end;
+ if(nb > 0){
+ if(nb < pool->quanta+1){
+ print("poolcompact: leftover too small\n");
+ abort();
+ }
+ end->size = nb;
+ B2T(end)->hdr = end;
+ pooladd(pool, end);
+ }
+ base = base->clink;
+ if(base == nil)
+ break;
+ ptr = B2NB(base);
+ end = ptr; /* could do better by copying between chains */
+ limit = B2LIMIT(base);
+ } else
+ ptr = next;
+ }
+
+ return compacted;
+}
+
+static void
+_poolfault(void *v, char *msg, ulong c)
+{
+ auditmemloc(msg, v);
+ panic("%s %lux (from %lux/%lux)", msg, v, getcallerpc(&v), c);
+}
+
+static void
+dumpvl(char *msg, ulong *v, int n)
+{
+ int i, l;
+
+ l = print("%s at %p: ", msg, v);
+ for (i = 0; i < n; i++) {
+ if (l >= 60) {
+ print("\n");
+ l = print(" %p: ", v);
+ }
+ l += print(" %lux", *v++);
+ }
+ print("\n");
+}
+
+static void
+corrupted(char *str, char *msg, Pool *p, Bhdr *b, void *v)
+{
+ print("%s(%p): pool %s CORRUPT: %s at %p'%lud(magic=%lux)\n",
+ str, v, p->name, msg, b, b->size, b->magic);
+ dumpvl("bad Bhdr", (ulong *)((ulong)b & ~3)-4, 10);
+}
+
+static void
+_auditmemloc(char *str, void *v)
+{
+ Pool *p;
+ Bhdr *bc, *ec, *b, *nb, *fb = nil;
+ char *fmsg, *msg;
+ ulong fsz;
+
+ SET(fsz);
+ SET(fmsg);
+ for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) {
+ lock(&p->l);
+ for (bc = p->chain; bc != nil; bc = bc->clink) {
+ if (bc->magic != MAGIC_E) {
+ unlock(&p->l);
+ corrupted(str, "chain hdr!=MAGIC_E", p, bc, v);
+ goto nextpool;
+ }
+ ec = B2LIMIT(bc);
+ if (((Bhdr*)v >= bc) && ((Bhdr*)v < ec))
+ goto found;
+ }
+ unlock(&p->l);
+nextpool: ;
+ }
+ print("%s: %p not in pools\n", str, v);
+ return;
+
+found:
+ for (b = bc; b < ec; b = nb) {
+ switch(b->magic) {
+ case MAGIC_F:
+ msg = "free blk";
+ break;
+ case MAGIC_I:
+ msg = "immutable block";
+ break;
+ case MAGIC_A:
+ msg = "block";
+ break;
+ default:
+ if (b == bc && b->magic == MAGIC_E) {
+ msg = "pool hdr";
+ break;
+ }
+ unlock(&p->l);
+ corrupted(str, "bad magic", p, b, v);
+ goto badchunk;
+ }
+ if (b->size <= 0 || (b->size & p->quanta)) {
+ unlock(&p->l);
+ corrupted(str, "bad size", p, b, v);
+ goto badchunk;
+ }
+ if (fb != nil)
+ break;
+ nb = B2NB(b);
+ if ((Bhdr*)v < nb) {
+ fb = b;
+ fsz = b->size;
+ fmsg = msg;
+ }
+ }
+ unlock(&p->l);
+ if (b >= ec) {
+ if (b > ec)
+ corrupted(str, "chain size mismatch", p, b, v);
+ else if (b->magic != MAGIC_E)
+ corrupted(str, "chain end!=MAGIC_E", p, b, v);
+ }
+badchunk:
+ if (fb != nil) {
+ print("%s: %p in %s:", str, v, p->name);
+ if (fb == v)
+ print(" is %s '%lux\n", fmsg, fsz);
+ else
+ print(" in %s at %p'%lux\n", fmsg, fb, fsz);
+ dumpvl("area", (ulong *)((ulong)v & ~3)-4, 20);
+ }
+}
+
+char *
+poolaudit(char*(*audit)(int, Bhdr *))
+{
+ Pool *p;
+ Bhdr *bc, *ec, *b;
+ char *r = nil;
+
+ for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) {
+ lock(&p->l);
+ for (bc = p->chain; bc != nil; bc = bc->clink) {
+ if (bc->magic != MAGIC_E) {
+ unlock(&p->l);
+ return "bad chain hdr";
+ }
+ ec = B2LIMIT(bc);
+ for (b = bc; b < ec; b = B2NB(b)) {
+ if (b->size <= 0 || (b->size & p->quanta))
+ r = "bad size in bhdr";
+ else
+ switch(b->magic) {
+ case MAGIC_E:
+ if (b != bc) {
+ r = "unexpected MAGIC_E";
+ break;
+ }
+ case MAGIC_F:
+ case MAGIC_A:
+ case MAGIC_I:
+ r = audit(p->pnum, b);
+ break;
+ default:
+ r = "bad magic";
+ }
+ if (r != nil) {
+ unlock(&p->l);
+ return r;
+ }
+ }
+ if (b != ec || b->magic != MAGIC_E) {
+ unlock(&p->l);
+ return "bad chain ending";
+ }
+ }
+ unlock(&p->l);
+ }
+ return r;
+}
--- /dev/null
+++ b/emu/port/audio-tbls.c
@@ -1,0 +1,53 @@
+svp_t audio_bits_tbl[] = {
+ { "8", 8 } , /* 8 bits per sample */
+ { "16", 16 }, /* 16 bits per sample */
+ {nil},
+};
+
+svp_t audio_chan_tbl[] = {
+ { "1", 1 }, /* 1 channel */
+ { "2", 2 }, /* 2 channels */
+ {nil},
+};
+
+svp_t audio_indev_tbl[] = {
+ { "mic", Audio_Mic_Val }, /* input microphone */
+ { "line", Audio_Linein_Val }, /* line in */
+ {nil},
+};
+
+svp_t audio_outdev_tbl[] = {
+ { "spkr", Audio_Speaker_Val }, /* output speaker */
+ { "hdph", Audio_Headphone_Val },/* head phones */
+ { "line", Audio_Lineout_Val }, /* line out */
+ {nil},
+};
+
+svp_t audio_enc_tbl[] = {
+ { "ulaw", Audio_Ulaw_Val }, /* u-law encoding */
+ { "alaw", Audio_Alaw_Val }, /* A-law encoding */
+ { "pcm", Audio_Pcm_Val }, /* Pulse Code Modulation */
+ {nil},
+};
+
+svp_t audio_rate_tbl[] = {
+ { "8000", 8000 }, /* 8000 samples per second */
+ { "11025", 11025 }, /* 11025 samples per second */
+ { "22050", 22050 }, /* 22050 samples per second */
+ { "44100", 44100 }, /* 44100 samples per second */
+ {nil},
+};
+
+Audio_d Default_Audio_Format = {
+ 0,
+ 16, /* bits per sample */
+ Audio_Max_Val, /* buffer size (as percentage) */
+ 2, /* number of channels */
+ -1, /* device */
+ Audio_Pcm_Val, /* encoding format */
+ 8000, /* samples per second */
+ Audio_Max_Val, /* left channel gain */
+ Audio_Max_Val, /* right channel gain */
+};
+int Default_Audio_Input = Audio_Mic_Val;
+int Default_Audio_Output = Audio_Speaker_Val;
--- /dev/null
+++ b/emu/port/audio.h
@@ -1,0 +1,81 @@
+#define AUDIO_BITS_FLAG 0x00000001
+#define AUDIO_BUF_FLAG 0x00000002
+#define AUDIO_CHAN_FLAG 0x00000004
+#define AUDIO_COUNT_FLAG 0x00000008
+#define AUDIO_DEV_FLAG 0x00000010
+#define AUDIO_ENC_FLAG 0x00000020
+#define AUDIO_RATE_FLAG 0x00000040
+#define AUDIO_VOL_FLAG 0x00000080
+#define AUDIO_LEFT_FLAG 0x00000100
+#define AUDIO_RIGHT_FLAG 0x00000200
+#define AUDIO_IN_FLAG 0x00000400
+#define AUDIO_OUT_FLAG 0x00000800
+#define AUDIO_MOD_FLAG 0x10000000
+
+#define Audio_Min_Val 0
+#define Audio_Max_Val 100
+
+#define Audio_No_Val 0
+#define Audio_In_Val 1
+#define Audio_Out_Val 2
+
+#define Audio_Max_Buf 32768
+#define Bits_Per_Byte 8
+
+typedef struct Audio_d Audio_d;
+struct Audio_d {
+ ulong flags; /* bit flag for fields */
+ ulong bits; /* bits per sample */
+ ulong buf; /* buffer size */
+ ulong chan; /* number of channels */
+ ulong dev; /* device */
+ ulong enc; /* encoding format */
+ ulong rate; /* samples per second */
+ ulong left; /* left channel gain */
+ ulong right; /* right channel gain */
+};
+
+typedef struct Audio_t Audio_t;
+struct Audio_t {
+ Audio_d in; /* input device */
+ Audio_d out; /* output device */
+};
+
+#define AUDIO_CMD_MAXNUM 32
+
+void audio_info_init(Audio_t*);
+int audioparse(char*, int n, Audio_t*);
+
+enum
+{
+ Qdir = 0, /* must start at 0 representing a directory */
+ Qaudio,
+ Qaudioctl
+};
+
+/* required external platform specific functions */
+void audio_file_init(void);
+void audio_file_open(Chan*, int);
+long audio_file_read(Chan*, void*, long, vlong);
+long audio_file_write(Chan*, void*, long, vlong);
+long audio_ctl_write(Chan*, void*, long, vlong);
+void audio_file_close(Chan*);
+
+typedef struct _svp_t {
+ char* s; /* string */
+ unsigned long v; /* value */
+} svp_t;
+
+/* string value pairs for default audio values */
+extern svp_t audio_chan_tbl[];
+extern svp_t audio_indev_tbl[];
+extern svp_t audio_outdev_tbl[];
+extern svp_t audio_enc_tbl[];
+extern svp_t audio_rate_tbl[];
+extern svp_t audio_val_tbl[];
+extern svp_t audio_bits_tbl[];
+
+extern Audio_d Default_Audio_Format;
+extern int Default_Audio_Input, Default_Audio_Output;
+
+extern Audio_t* getaudiodev(void);
--- /dev/null
+++ b/emu/port/cache.c
@@ -1,0 +1,45 @@
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * no cache in hosted mode
+ */
+void
+cinit(void)
+{
+}
+
+void
+copen(Chan *c)
+{
+ c->flag &= ~CCACHE;
+}
+
+int
+cread(Chan *c, uchar *b, int n, vlong off)
+{
+ USED(c);
+ USED(b);
+ USED(n);
+ USED(off);
+ return 0;
+}
+
+void
+cwrite(Chan *c, uchar *buf, int n, vlong off)
+{
+ USED(c);
+ USED(buf);
+ USED(n);
+ USED(off);
+}
+
+void
+cupdate(Chan *c, uchar *buf, int n, vlong off)
+{
+ USED(c);
+ USED(buf);
+ USED(n);
+ USED(off);
+}
+
--- /dev/null
+++ b/emu/port/chan.c
@@ -1,0 +1,1403 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+char*
+c2name(Chan *c) /* DEBUGGING */
+{
+ if(c == nil)
+ return "<nil chan>";
+ if(c->name == nil)
+ return "<nil name>";
+ if(c->name->s == nil)
+ return "<nil name.s>";
+ return c->name->s;
+}
+
+enum
+{
+ CNAMESLOP = 20
+};
+
+struct
+{
+ Lock l;
+ int fid;
+ Chan *free;
+ Chan *list;
+}chanalloc;
+
+typedef struct Elemlist Elemlist;
+
+struct Elemlist
+{
+ char *name; /* copy of name, so '/' can be overwritten */
+ int nelems;
+ char **elems;
+ int *off;
+ int mustbedir;
+};
+
+#define SEP(c) ((c) == 0 || (c) == '/')
+void cleancname(Cname*);
+
+int
+isdotdot(char *p)
+{
+ return p[0]=='.' && p[1]=='.' && p[2]=='\0';
+}
+
+int
+incref(Ref *r)
+{
+ int x;
+
+ lock(&r->lk);
+ x = ++r->ref;
+ unlock(&r->lk);
+ return x;
+}
+
+int
+decref(Ref *r)
+{
+ int x;
+
+ lock(&r->lk);
+ x = --r->ref;
+ unlock(&r->lk);
+ if(x < 0)
+ panic("decref, pc=0x%lux", getcallerpc(&r));
+
+ return x;
+}
+
+/*
+ * Rather than strncpy, which zeros the rest of the buffer, kstrcpy
+ * truncates if necessary, always zero terminates, does not zero fill,
+ * and puts ... at the end of the string if it's too long. Usually used to
+ * save a string in up->genbuf;
+ */
+void
+kstrcpy(char *s, char *t, int ns)
+{
+ int nt;
+
+ nt = strlen(t);
+ if(nt+1 <= ns){
+ memmove(s, t, nt+1);
+ return;
+ }
+ /* too long */
+ if(ns < 4){
+ /* but very short! */
+ strncpy(s, t, ns);
+ return;
+ }
+ /* truncate with ... at character boundary (very rare case) */
+ memmove(s, t, ns-4);
+ ns -= 4;
+ s[ns] = '\0';
+ /* look for first byte of UTF-8 sequence by skipping continuation bytes */
+ while(ns>0 && (s[--ns]&0xC0)==0x80)
+ ;
+ strcpy(s+ns, "...");
+}
+
+int
+emptystr(char *s)
+{
+ return s == nil || s[0] == '\0';
+}
+
+/*
+ * Atomically replace *p with copy of s
+ */
+void
+kstrdup(char **p, char *s)
+{
+ int n;
+ char *t, *prev;
+
+ n = strlen(s)+1;
+ t = kmalloc(n);
+ if(t == nil)
+ panic("kstrdup: no memory");
+ memmove(t, s, n);
+ prev = *p;
+ *p = t;
+ free(prev);
+}
+
+static char isfrog[256]=
+{
+ /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1,
+ /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1,
+ /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1,
+ /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1
+};
+
+void
+chandevinit(void)
+{
+ int i;
+
+
+/* isfrog[' '] = 1; */ /* let's see what happens */
+ isfrog['/'] = 1;
+ isfrog[0x7f] = 1;
+
+ for(i=0; devtab[i] != nil; i++)
+ devtab[i]->init();
+}
+
+Chan*
+newchan(void)
+{
+ Chan *c;
+
+ lock(&chanalloc.l);
+ c = chanalloc.free;
+ if(c != 0)
+ chanalloc.free = c->next;
+ unlock(&chanalloc.l);
+
+ if(c == nil) {
+ c = malloc(sizeof(Chan));
+ if(c == nil)
+ error(Enomem);
+ lock(&chanalloc.l);
+ c->fid = ++chanalloc.fid;
+ c->link = chanalloc.list;
+ chanalloc.list = c;
+ unlock(&chanalloc.l);
+ }
+
+ /* if you get an error before associating with a dev,
+ close calls rootclose, a nop */
+ c->type = 0;
+ c->flag = 0;
+ c->r.ref = 1;
+ c->dev = 0;
+ c->offset = 0;
+ c->iounit = 0;
+ c->umh = 0;
+ c->uri = 0;
+ c->dri = 0;
+ c->aux = 0;
+ c->mchan = 0;
+ c->mcp = 0;
+ c->mux = 0;
+ c->mqid.path = 0;
+ c->mqid.vers = 0;
+ c->mqid.type = 0;
+ c->name = 0;
+ return c;
+}
+
+static Ref ncname;
+
+Cname*
+newcname(char *s)
+{
+ Cname *n;
+ int i;
+
+ n = smalloc(sizeof(Cname));
+ i = strlen(s);
+ n->len = i;
+ n->alen = i+CNAMESLOP;
+ n->s = smalloc(n->alen);
+ memmove(n->s, s, i+1);
+ n->r.ref = 1;
+ incref(&ncname);
+ return n;
+}
+
+void
+cnameclose(Cname *n)
+{
+ if(n == nil)
+ return;
+ if(decref(&n->r))
+ return;
+ decref(&ncname);
+ free(n->s);
+ free(n);
+}
+
+Cname*
+addelem(Cname *n, char *s)
+{
+ int i, a;
+ char *t;
+ Cname *new;
+
+ if(s[0]=='.' && s[1]=='\0')
+ return n;
+
+ if(n->r.ref > 1){
+ /* copy on write */
+ new = newcname(n->s);
+ cnameclose(n);
+ n = new;
+ }
+
+ i = strlen(s);
+ if(n->len+1+i+1 > n->alen){
+ a = n->len+1+i+1 + CNAMESLOP;
+ t = smalloc(a);
+ memmove(t, n->s, n->len+1);
+ free(n->s);
+ n->s = t;
+ n->alen = a;
+ }
+ if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/') /* don't insert extra slash if one is present */
+ n->s[n->len++] = '/';
+ memmove(n->s+n->len, s, i+1);
+ n->len += i;
+ if(isdotdot(s))
+ cleancname(n);
+ return n;
+}
+
+void
+chanfree(Chan *c)
+{
+ c->flag = CFREE;
+
+ if(c->umh != nil){
+ putmhead(c->umh);
+ c->umh = nil;
+ }
+ if(c->umc != nil){
+ cclose(c->umc);
+ c->umc = nil;
+ }
+ if(c->mux != nil){
+ muxclose(c->mux);
+ c->mux = nil;
+ }
+ if(c->mchan != nil){
+ cclose(c->mchan);
+ c->mchan = nil;
+ }
+
+ cnameclose(c->name);
+
+ lock(&chanalloc.l);
+ c->next = chanalloc.free;
+ chanalloc.free = c;
+ unlock(&chanalloc.l);
+}
+
+void
+cclose(Chan *c)
+{
+ if(c == 0)
+ return;
+
+ if(c->flag&CFREE)
+ panic("cclose %lux", getcallerpc(&c));
+
+ if(decref(&c->r))
+ return;
+
+ if(!waserror()){
+ devtab[c->type]->close(c);
+ poperror();
+ }
+
+ chanfree(c);
+}
+
+/*
+ * Make sure we have the only copy of c. (Copy on write.)
+ */
+Chan*
+cunique(Chan *c)
+{
+ Chan *nc;
+
+ if(c->r.ref != 1) {
+ nc = cclone(c);
+ cclose(c);
+ c = nc;
+ }
+
+ return c;
+}
+
+int
+eqqid(Qid a, Qid b)
+{
+ return a.path==b.path && a.vers==b.vers;
+}
+
+int
+eqchan(Chan *a, Chan *b, int pathonly)
+{
+ if(a->qid.path != b->qid.path)
+ return 0;
+ if(!pathonly && a->qid.vers!=b->qid.vers)
+ return 0;
+ if(a->type != b->type)
+ return 0;
+ if(a->dev != b->dev)
+ return 0;
+ return 1;
+}
+
+int
+eqchantdqid(Chan *a, int type, int dev, Qid qid, int pathonly)
+{
+ if(a->qid.path != qid.path)
+ return 0;
+ if(!pathonly && a->qid.vers!=qid.vers)
+ return 0;
+ if(a->type != type)
+ return 0;
+ if(a->dev != dev)
+ return 0;
+ return 1;
+}
+
+Mhead*
+newmhead(Chan *from)
+{
+ Mhead *mh;
+
+ mh = smalloc(sizeof(Mhead));
+ mh->r.ref = 1;
+ mh->from = from;
+ incref(&from->r);
+
+/*
+ n = from->name->len;
+ if(n >= sizeof(mh->fromname))
+ n = sizeof(mh->fromname)-1;
+ memmove(mh->fromname, from->name->s, n);
+ mh->fromname[n] = 0;
+*/
+ return mh;
+}
+
+int
+cmount(Chan *new, Chan *old, int flag, char *spec)
+{
+ Pgrp *pg;
+ int order, flg;
+ Mhead *m, **l, *mh;
+ Mount *nm, *f, *um, **h;
+
+ if(QTDIR & (old->qid.type^new->qid.type))
+ error(Emount);
+
+if(old->umh)
+ print("cmount old extra umh\n");
+
+ order = flag&MORDER;
+
+ if((old->qid.type&QTDIR)==0 && order != MREPL)
+ error(Emount);
+
+ mh = new->umh;
+
+ /*
+ * Not allowed to bind when the old directory
+ * is itself a union. (Maybe it should be allowed, but I don't see
+ * what the semantics would be.)
+ *
+ * We need to check mh->mount->next to tell unions apart from
+ * simple mount points, so that things like
+ * mount -c fd /root
+ * bind -c /root /
+ * work. The check of mount->mflag catches things like
+ * mount fd /root
+ * bind -c /root /
+ *
+ * This is far more complicated than it should be, but I don't
+ * see an easier way at the moment. -rsc
+ */
+ if((flag&MCREATE) && mh && mh->mount
+ && (mh->mount->next || !(mh->mount->mflag&MCREATE)))
+ error(Emount);
+
+ pg = up->env->pgrp;
+ wlock(&pg->ns);
+
+ l = &MOUNTH(pg, old->qid);
+ for(m = *l; m; m = m->hash) {
+ if(eqchan(m->from, old, 1))
+ break;
+ l = &m->hash;
+ }
+
+ if(m == nil) {
+ /*
+ * nothing mounted here yet. create a mount
+ * head and add to the hash table.
+ */
+ m = newmhead(old);
+ *l = m;
+
+ /*
+ * if this is a union mount, add the old
+ * node to the mount chain.
+ */
+ if(order != MREPL)
+ m->mount = newmount(m, old, 0, 0);
+ }
+ wlock(&m->lock);
+ if(waserror()){
+ wunlock(&m->lock);
+ nexterror();
+ }
+ wunlock(&pg->ns);
+
+ nm = newmount(m, new, flag, spec);
+ if(mh != nil && mh->mount != nil) {
+ /*
+ * copy a union when binding it onto a directory
+ */
+ flg = order;
+ if(order == MREPL)
+ flg = MAFTER;
+ h = &nm->next;
+ um = mh->mount;
+ for(um = um->next; um; um = um->next) {
+ f = newmount(m, um->to, flg, um->spec);
+ *h = f;
+ h = &f->next;
+ }
+ }
+
+ if(m->mount && order == MREPL) {
+ mountfree(m->mount);
+ m->mount = 0;
+ }
+
+ if(flag & MCREATE)
+ nm->mflag |= MCREATE;
+
+ if(m->mount && order == MAFTER) {
+ for(f = m->mount; f->next; f = f->next)
+ ;
+ f->next = nm;
+ }
+ else {
+ for(f = nm; f->next; f = f->next)
+ ;
+ f->next = m->mount;
+ m->mount = nm;
+ }
+
+ wunlock(&m->lock);
+ poperror();
+ return nm->mountid;
+}
+
+void
+cunmount(Chan *mnt, Chan *mounted)
+{
+ Pgrp *pg;
+ Mhead *m, **l;
+ Mount *f, **p;
+
+ if(mnt->umh) /* should not happen */
+ print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh);
+
+ /*
+ * It _can_ happen that mounted->umh is non-nil,
+ * because mounted is the result of namec(Aopen)
+ * (see sysfile.c:/^sysunmount).
+ * If we open a union directory, it will have a umh.
+ * Although surprising, this is okay, since the
+ * cclose will take care of freeing the umh.
+ */
+
+ pg = up->env->pgrp;
+ wlock(&pg->ns);
+
+ l = &MOUNTH(pg, mnt->qid);
+ for(m = *l; m; m = m->hash) {
+ if(eqchan(m->from, mnt, 1))
+ break;
+ l = &m->hash;
+ }
+
+ if(m == 0) {
+ wunlock(&pg->ns);
+ error(Eunmount);
+ }
+
+ wlock(&m->lock);
+ if(mounted == 0) {
+ *l = m->hash;
+ wunlock(&pg->ns);
+ mountfree(m->mount);
+ m->mount = nil;
+ cclose(m->from);
+ wunlock(&m->lock);
+ putmhead(m);
+ return;
+ }
+
+ p = &m->mount;
+ for(f = *p; f; f = f->next) {
+ /* BUG: Needs to be 2 pass */
+ if(eqchan(f->to, mounted, 1) ||
+ (f->to->mchan && eqchan(f->to->mchan, mounted, 1))) {
+ *p = f->next;
+ f->next = 0;
+ mountfree(f);
+ if(m->mount == nil) {
+ *l = m->hash;
+ cclose(m->from);
+ wunlock(&m->lock);
+ wunlock(&pg->ns);
+ putmhead(m);
+ return;
+ }
+ wunlock(&m->lock);
+ wunlock(&pg->ns);
+ return;