summaryrefslogtreecommitdiff
path: root/emu/Nt
diff options
context:
space:
mode:
Diffstat (limited to 'emu/Nt')
-rw-r--r--emu/Nt/audio.c810
-rw-r--r--emu/Nt/cmd.c245
-rw-r--r--emu/Nt/devarch.c414
-rw-r--r--emu/Nt/deveia.c672
-rw-r--r--emu/Nt/devfs.c2507
-rw-r--r--emu/Nt/emu108
-rw-r--r--emu/Nt/fp.c107
-rw-r--r--emu/Nt/ie104
-rw-r--r--emu/Nt/ie-os.c847
-rw-r--r--emu/Nt/ie-win.c223
-rw-r--r--emu/Nt/ieplugin.h75
-rw-r--r--emu/Nt/ipif.c407
-rw-r--r--emu/Nt/mkfile51
-rw-r--r--emu/Nt/nt.rc1
-rw-r--r--emu/Nt/os.c875
-rw-r--r--emu/Nt/vlrt.c764
-rw-r--r--emu/Nt/win.c841
17 files changed, 9051 insertions, 0 deletions
diff --git a/emu/Nt/audio.c b/emu/Nt/audio.c
new file mode 100644
index 00000000..e1bc6f86
--- /dev/null
+++ b/emu/Nt/audio.c
@@ -0,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;
+}
diff --git a/emu/Nt/cmd.c b/emu/Nt/cmd.c
new file mode 100644
index 00000000..a03b949b
--- /dev/null
+++ b/emu/Nt/cmd.c
@@ -0,0 +1,245 @@
+#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 *rpfd, int *wpfd)
+{
+ STARTUPINFO si;
+ SECURITY_ATTRIBUTES sec;
+ HANDLE rh, wh, srh, swh;
+ 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;
+ if(!CreatePipe(&rh, &swh, &sec, 0)) {
+ print("can't create pipe\n");
+ free(cmd);
+ free(wcmd);
+ free(wdir);
+ return nil;
+ }
+ if(!CreatePipe(&srh, &wh, &sec, 0)) {
+ print("can't create pipe\n");
+ CloseHandle(rh);
+ CloseHandle(swh);
+ free(cmd);
+ free(wcmd);
+ free(wdir);
+ return nil;
+ }
+ rh = exporthandle(rh, 1);
+ wh = exporthandle(wh, 1);
+ if (rh == nil || wh == nil) {
+ print("can't dup pipes\n");
+ CloseHandle(rh);
+ CloseHandle(swh);
+ CloseHandle(wh);
+ CloseHandle(srh);
+ free(cmd);
+ free(wcmd);
+ free(wdir);
+ return nil;
+ }
+
+ 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 = exporthandle(wh, 0);
+
+ 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());
+ CloseHandle(si.hStdInput);
+ CloseHandle(swh);
+ CloseHandle(si.hStdOutput);
+ CloseHandle(si.hStdError);
+ CloseHandle(srh);
+ free(cmd);
+ free(wcmd);
+ free(wdir);
+ return nil;
+ }
+
+ *rpfd = nth2fd(srh);
+ *wpfd = nth2fd(swh);
+ if(*wpfd == 1 || *wpfd == 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;
+}
+
+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);
+}
diff --git a/emu/Nt/devarch.c b/emu/Nt/devarch.c
new file mode 100644
index 00000000..2ddaf763
--- /dev/null
+++ b/emu/Nt/devarch.c
@@ -0,0 +1,414 @@
+/*
+ * 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"
+
+
+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;
+ Rune utf[1]; /* more allocated as required */
+ char data[1];
+ };
+};
+
+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;
+
+extern wchar_t *widen(char*);
+static Value* getregistry(HKEY, Rune*, Rune*);
+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->utf);
+ 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->utf);
+ 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;
+ Rune *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->utf[0])
+ snprint(p, READSTR, "str %Q", v->utf);
+ 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->utf+i);
+ while(v->utf[i++] != 0){
+ /* skip */
+ }
+ if(v->utf[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;
+ Rune *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)
+{
+ Rune err[64];
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ err, sizeof(err), 0);
+ errorf("%S", err);
+}
+
+static Value*
+getregistry(HKEY root, Rune *keyname, Rune *name)
+{
+ long res;
+ HKEY key;
+ DWORD dtype, n;
+ void* vp;
+ Value *val;
+
+ qlock(&reglock);
+ if(waserror()){
+ qunlock(&reglock);
+ 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);
+ val = smalloc(sizeof(Value)+n);
+ if(waserror()){
+ free(val);
+ nexterror();
+ }
+ if(0)
+ fprint(2, "%S\\%S: %d %d\n", keyname, name, dtype, n);
+ 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 = val->utf;
+ 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();
+ poperror();
+ RegCloseKey(key);
+ poperror();
+ qunlock(&reglock);
+ return val;
+}
diff --git a/emu/Nt/deveia.c b/emu/Nt/deveia.c
new file mode 100644
index 00000000..4294b8fe
--- /dev/null
+++ b/emu/Nt/deveia.c
@@ -0,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 = calloc(ndir, sizeof(Dirtab));
+ if(dp == 0)
+ panic("eiainit");
+ eia = calloc(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;
+}
diff --git a/emu/Nt/devfs.c b/emu/Nt/devfs.c
new file mode 100644
index 00000000..c15b4549
--- /dev/null
+++ b/emu/Nt/devfs.c
@@ -0,0 +1,2507 @@
+#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 <lm.h>
+
+/* TODO: try using / in place of \ in path names */
+
+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;
+ Rune* 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;
+ Rune *name;
+ Rune *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";
+ Rune rootname[] = L"inferno-server";
+static Qid rootqid;
+static User *fsnone;
+static User *fsuser;
+static Rune *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";
+
+/*
+ * these lan manager functions are not supplied
+ * on windows95, so we have to load the dll by hand
+ */
+static struct {
+ NET_API_STATUS (NET_API_FUNCTION *UserGetLocalGroups)(
+ LPWSTR servername,
+ LPWSTR username,
+ DWORD level,
+ DWORD flags,
+ LPBYTE *bufptr,
+ DWORD prefmaxlen,
+ LPDWORD entriesread,
+ LPDWORD totalentries);
+ NET_API_STATUS (NET_API_FUNCTION *UserGetGroups)(
+ LPWSTR servername,
+ LPWSTR username,
+ DWORD level,
+ LPBYTE *bufptr,
+ DWORD prefmaxlen,
+ LPDWORD entriesread,
+ LPDWORD totalentries);
+ NET_API_STATUS (NET_API_FUNCTION *GetAnyDCName)(
+ LPCWSTR ServerName,
+ LPCWSTR DomainName,
+ LPBYTE *Buffer);
+ NET_API_STATUS (NET_API_FUNCTION *ApiBufferFree)(LPVOID Buffer);
+} net;
+
+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*, Rune*);
+static int secsize(char*, Rune*);
+static void seccheck(char*, ulong, Rune*);
+static int sechasperm(char*, ulong, Rune*);
+static SECURITY_DESCRIPTOR* secsd(char*, char[SD_ROCK]);
+static int secsdhasperm(SECURITY_DESCRIPTOR*, ulong, Rune*);
+static int secsdstat(SECURITY_DESCRIPTOR*, Stat*, Rune*);
+static SECURITY_DESCRIPTOR* secmksd(char[SD_ROCK], Stat*, ACL*, int);
+static SID *dupsid(SID*);
+static int ismembersid(Rune*, User*, SID*);
+static int ismember(User*, User*);
+static User *sidtouser(Rune*, SID*);
+static User *domnametouser(Rune*, Rune*, Rune*);
+static User *nametouser(Rune*, Rune*);
+static User *unametouser(Rune*, char*);
+static void addgroups(User*, int);
+static User *mkuser(SID*, int, Rune*, Rune*);
+static Rune *domsrv(Rune *, Rune[MAX_PATH]);
+static Rune *filesrv(char*);
+static int fsacls(char*);
+static User *secuser(void);
+
+ int runeslen(Rune*);
+ Rune* runesdup(Rune*);
+ Rune* utftorunes(Rune*, char*, int);
+ char* runestoutf(char*, Rune*, int);
+ int runescmp(Rune*, Rune*);
+
+
+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] == '.' && runeslen(data->cFileName) == 1)
+ || runescmp(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);
+ runestoutf(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);
+ 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(&current->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(&current->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(1 || offset <= 0x7ffffff){ /* TO DO: remove 1 || */
+ 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;
+ utftorunes(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);
+ }
+ utftorunes(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);
+ utftorunes(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);
+ utftorunes(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 = runescmp(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);
+ utftorunes(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 = runescmp(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);
+ utftorunes(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);
+ utftorunes(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;
+ }
+ runestoutf(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;
+ }
+ runestoutf(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)
+{
+ HMODULE lib;
+ 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;
+
+ lib = LoadLibraryA("netapi32");
+ if(lib == 0) {
+ usesec = 0;
+ return;
+ }
+
+ net.UserGetGroups = (void*)GetProcAddress(lib, "NetUserGetGroups");
+ if(net.UserGetGroups == 0)
+ panic("bad netapi32 library");
+ net.UserGetLocalGroups = (void*)GetProcAddress(lib, "NetUserGetLocalGroups");
+ if(net.UserGetLocalGroups == 0)
+ panic("bad netapi32 library");
+ net.GetAnyDCName = (void*)GetProcAddress(lib, "NetGetAnyDCName");
+ if(net.GetAnyDCName == 0)
+ panic("bad netapi32 library");
+ net.ApiBufferFree = (void*)GetProcAddress(lib, "NetApiBufferFree");
+ if(net.ApiBufferFree == 0)
+ panic("bad netapi32 library");
+
+ 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(runescmp(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, Rune *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 = runenlen(st.owner->name, runeslen(st.owner->name));
+ dir->uid = smalloc(n+1);
+ runestoutf(dir->uid, st.owner->name, n+1);
+ n = runenlen(st.group->name, runeslen(st.group->name));
+ dir->gid = smalloc(n+1);
+ runestoutf(dir->gid, st.group->name, n+1);
+ }
+ return ok;
+}
+
+static int
+secsize(char *file, Rune *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 runenlen(st.owner->name, runeslen(st.owner->name))+runenlen(st.group->name, runeslen(st.group->name));
+ return -1;
+}
+
+/*
+ * verify that u had access to file
+ */
+static void
+seccheck(char *file, ulong access, Rune *srv)
+{
+ if(!sechasperm(file, access, srv))
+ error(Eperm);
+}
+
+static int
+sechasperm(char *file, ulong access, Rune *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, Rune *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);
+ group = sidtouser(srv, gsid);
+ if(owner == 0 || group == 0)
+ 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, Rune *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(Rune *srv, SID *s)
+{
+ SID_NAME_USE type;
+ Rune 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 nil;
+ return mkuser(s, type, aname, dname);
+}
+
+static User*
+domnametouser(Rune *srv, Rune *name, Rune *dom)
+{
+ User *u;
+
+ qlock(&users.lk);
+ for(u = users.u; u != 0; u = u->next)
+ if(runescmp(name, u->name) == 0 && runescmp(dom, u->dom) == 0)
+ break;
+ qunlock(&users.lk);
+ if(u == 0)
+ u = nametouser(srv, name);
+ return u;
+}
+
+static User*
+nametouser(Rune *srv, Rune *name)
+{
+ char sidrock[MAX_SID];
+ SID *sid;
+ SID_NAME_USE type;
+ Rune 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(Rune *srv, char *name)
+{
+ Rune rname[MAX_PATH];
+
+ utftorunes(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, Rune *name, Rune *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 = runesdup(name);
+ u->dom = nil;
+ if(dom != nil)
+ u->dom = runesdup(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(Rune *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;
+ Rune *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;
+
+ rem = 1;
+ n = 0;
+ srv = domsrv(u->dom, srvrock);
+ while(rem != n){
+ i = net.UserGetGroups(srv, u->name, 0,
+ (BYTE**)&grp, 1024, &n, &rem);
+ if(i != NERR_Success && i != ERROR_MORE_DATA)
+ break;
+ 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;
+ }
+ net.ApiBufferFree(grp);
+ }
+
+ rem = 1;
+ n = 0;
+ while(rem != n){
+ i = net.UserGetLocalGroups(srv, u->name, 0, LG_INCLUDE_INDIRECT,
+ (BYTE**)&loc, 1024, &n, &rem);
+ if(i != NERR_Success && i != ERROR_MORE_DATA)
+ break;
+ 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;
+ }
+ net.ApiBufferFree(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 Rune*
+filesrv(char *file)
+{
+ int n;
+ Rune *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;
+ runestoutf(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(Rune));
+ if(srv == nil)
+ panic("filesrv: no memory");
+ utftorunes(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;
+ }
+ utftorunes(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 Rune*
+domsrv(Rune *dom, Rune srv[MAX_PATH])
+{
+ Rune *psrv;
+ int n, r;
+
+ if(dom[0] == 0)
+ return nil;
+
+ r = net.GetAnyDCName(NULL, dom, (LPBYTE*)&psrv);
+ if(r == NERR_Success) {
+ n = runeslen(psrv);
+ if(n >= MAX_PATH)
+ n = MAX_PATH-1;
+ memmove(srv, psrv, n*sizeof(Rune));
+ srv[n] = 0;
+ net.ApiBufferFree(psrv);
+ return srv;
+ }
+
+ return nil;
+}
+
+Rune*
+runesdup(Rune *r)
+{
+ int n;
+ Rune *s;
+
+ n = runeslen(r) + 1;
+ s = malloc(n * sizeof(Rune));
+ if(s == nil)
+ error(Enomem);
+ memmove(s, r, n * sizeof(Rune));
+ return s;
+}
+
+int
+runeslen(Rune *r)
+{
+ int n;
+
+ n = 0;
+ while(*r++ != 0)
+ n++;
+ return n;
+}
+
+char*
+runestoutf(char *p, Rune *r, int nc)
+{
+ char *op, *ep;
+ int n, c;
+
+ op = p;
+ ep = p + nc;
+ while(c = *r++) {
+ n = 1;
+ if(c >= Runeself)
+ n = runelen(c);
+ if(p + n >= ep)
+ break;
+ if(c < Runeself)
+ *p++ = c;
+ else
+ p += runetochar(p, r-1);
+ }
+ *p = '\0';
+ return op;
+}
+
+Rune*
+utftorunes(Rune *r, char *p, int nc)
+{
+ Rune *or, *er;
+
+ or = r;
+ er = r + nc;
+ while(*p != '\0' && r + 1 < er)
+ p += chartorune(r++, p);
+ *r = '\0';
+ return or;
+}
+
+int
+runescmp(Rune *s1, Rune *s2)
+{
+ Rune r1, r2;
+
+ for(;;) {
+ r1 = *s1++;
+ r2 = *s2++;
+ if(r1 != r2) {
+ if(r1 > r2)
+ return 1;
+ return -1;
+ }
+ if(r1 == 0)
+ return 0;
+ }
+}
+
+Dev fsdevtab = {
+ 'U',
+ "fs",
+
+ fsinit,
+ fsattach,
+ fswalk,
+ fsstat,
+ fsopen,
+ fscreate,
+ fsclose,
+ fsread,
+ devbread,
+ fswrite,
+ devbwrite,
+ fsremove,
+ fswstat
+};
diff --git a/emu/Nt/emu b/emu/Nt/emu
new file mode 100644
index 00000000..d11ae848
--- /dev/null
+++ b/emu/Nt/emu
@@ -0,0 +1,108 @@
+env
+ LDFLAGS= -subsystem:console -entry:WinMainCRTStartup $LDFLAGS
+
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ draw
+
+ 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
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ print
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /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 /
diff --git a/emu/Nt/fp.c b/emu/Nt/fp.c
new file mode 100644
index 00000000..586c5964
--- /dev/null
+++ b/emu/Nt/fp.c
@@ -0,0 +1,107 @@
+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);
+}
+
+
+
+
+
diff --git a/emu/Nt/ie b/emu/Nt/ie
new file mode 100644
index 00000000..e2d3d218
--- /dev/null
+++ b/emu/Nt/ie
@@ -0,0 +1,104 @@
+env
+ LDFLAGS= -subsystem:windows $LDFLAGS
+ WINX= ie-win
+ OSX= ie-os
+
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ indir
+
+ draw
+
+ ip ipif ipaux
+ eia
+ audio audio
+ mem
+# arch
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+ memlayer
+ memdraw
+ keyring
+ crypt
+ 9
+
+link
+
+mod
+ sys
+ draw
+ tk
+ math
+ srv srv
+ keyring
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ print
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /root /
+ /root/dev /
+ /root/fd /
+ /root/prog /
+ /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
diff --git a/emu/Nt/ie-os.c b/emu/Nt/ie-os.c
new file mode 100644
index 00000000..20641a95
--- /dev/null
+++ b/emu/Nt/ie-os.c
@@ -0,0 +1,847 @@
+#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 "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);
+
+ wchar_t *widen(char *s);
+ char *narrowen(wchar_t *ws);
+ int widebytes(wchar_t *ws);
+ int runeslen(Rune*);
+ Rune* runesdup(Rune*);
+ Rune* utftorunes(Rune*, char*, int);
+ char* runestoutf(char*, Rune*, int);
+ int runescmp(Rune*, Rune*);
+
+_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;
+}
+
+int
+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){
+ print("out of kernel processes\n");
+ return -1;
+ }
+
+ 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){
+ pfree(p);
+ print("ran out of kernel processes\n");
+ return -1;
+ }
+ return p->pid;
+}
+
+#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;
+}
+
+long
+time(long *tp)
+{
+ SYSTEMTIME tm;
+ long t;
+
+ GetSystemTime(&tm);
+ t = tm2sec(&tm);
+ if(tp != nil)
+ *tp = t;
+ return t;
+}
+
+/*
+ * 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;
+}
+
+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;
+}
+
+wchar_t *
+widen(char *s)
+{
+ int n;
+ wchar_t *ws;
+
+ n = utflen(s) + 1;
+ ws = smalloc(n*sizeof(wchar_t));
+ utftorunes(ws, s, n);
+ return ws;
+}
+
+
+char *
+narrowen(wchar_t *ws)
+{
+ char *s;
+ int n;
+
+ n = widebytes(ws);
+ s = smalloc(n);
+ runestoutf(s, ws, n);
+ return s;
+}
+
+
+int
+widebytes(wchar_t *ws)
+{
+ int n = 0;
+
+ while (*ws)
+ n += runelen(*ws++);
+ return n+1;
+}
diff --git a/emu/Nt/ie-win.c b/emu/Nt/ie-win.c
new file mode 100644
index 00000000..93abc4d4
--- /dev/null
+++ b/emu/Nt/ie-win.c
@@ -0,0 +1,223 @@
+#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;
+}
+
+void
+clipwrite(char *p)
+{
+ USED(p);
+{
diff --git a/emu/Nt/ieplugin.h b/emu/Nt/ieplugin.h
new file mode 100644
index 00000000..bb8db90c
--- /dev/null
+++ b/emu/Nt/ieplugin.h
@@ -0,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)
diff --git a/emu/Nt/ipif.c b/emu/Nt/ipif.c
new file mode 100644
index 00000000..eab9dd7f
--- /dev/null
+++ b/emu/Nt/ipif.c
@@ -0,0 +1,407 @@
+#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"
+
+extern int SOCK_SELECT;
+
+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;
+ char *h = hdr;
+
+
+ osenter();
+ if(hdr == 0)
+ r = send(sock, va, len, 0);
+ else {
+ memset(&sa, sizeof(sa), 0);
+ 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, l;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+ char 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, sizeof h, 0);
+ switch(hdrlen){
+ case OUdphdrlenv4:
+ memmove(h, &sin->sin_addr, 4);
+ 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. */
+ 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, unsigned long raddr, unsigned short rport)
+{
+ int r;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ memset(&sa, 0, sizeof(sa));
+ sin = (struct sockaddr_in*)&sa;
+ sin->sin_family = AF_INET;
+ hnputs(&sin->sin_port, rport);
+ hnputl(&sin->sin_addr.s_addr, raddr);
+
+ osenter();
+ r = connect(fd, &sa, sizeof(sa));
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+void
+so_getsockname(int fd, unsigned long *laddr, unsigned short *lport)
+{
+ int len;
+ struct sockaddr sa;
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in*)&sa;
+
+ len = sizeof(sa);
+ if(getsockname(fd, &sa, &len) < 0)
+ oserror();
+
+ if(sin->sin_family != AF_INET || len != sizeof(*sin))
+ error("not AF_INET");
+
+ *laddr = nhgetl(&sin->sin_addr.s_addr);
+ *lport = nhgets(&sin->sin_port);
+}
+
+void
+so_listen(int fd)
+{
+ int r;
+
+ osenter();
+ r = listen(fd, 5);
+ osleave();
+ if(r < 0)
+ oserror();
+}
+
+int
+so_accept(int fd, unsigned long *raddr, unsigned short *rport)
+{
+ int nfd, 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("not AF_INET");
+
+ *raddr = nhgetl(&sin->sin_addr.s_addr);
+ *rport = nhgets(&sin->sin_port);
+ return nfd;
+}
+
+void
+so_bind(int fd, int su, unsigned short 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;
+ hnputs(&sin->sin_port, i);
+
+ if(bind(fd, &sa, sizeof(sa)) >= 0)
+ return;
+ }
+ oserror();
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sin->sin_family = AF_INET;
+ hnputs(&sin->sin_port, port);
+
+ if(bind(fd, &sa, sizeof(sa)) < 0)
+ oserror();
+}
+
+void
+so_setsockopt(int fd, int opt, int value)
+{
+ int r;
+ struct linger l;
+
+ if(opt == SO_LINGER){
+ l.l_onoff = 1;
+ l.l_linger = (short) value;
+ osenter();
+ r = setsockopt(fd, SOL_SOCKET, opt, (char *)&l, sizeof(l));
+ osleave();
+ }else
+ error(Ebadctl);
+ if(r < 0)
+ oserror();
+}
+
+int
+so_gethostbyname(char *host, char**hostv, int n)
+{
+ int i;
+ unsigned char buf[32], *p;
+ struct hostent *hp;
+
+ hp = gethostbyname(host);
+ if(hp == 0)
+ return 0;
+
+ for(i = 0; hp->h_addr_list[i] && i < n; i++) {
+ p = 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 linger)
+{
+ int r;
+ static struct linger l = {1, 1000};
+
+ osenter();
+ if(linger)
+ 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));
+}
diff --git a/emu/Nt/mkfile b/emu/Nt/mkfile
new file mode 100644
index 00000000..30acf67c
--- /dev/null
+++ b/emu/Nt/mkfile
@@ -0,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
+WINX=win
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ $OSX.$O\
+ $WINX.$O\
+ $CONF.root.$O\
+ lock.$O\
+ fp.$O\
+ vlrt.$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= $SYSLIBS 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
diff --git a/emu/Nt/nt.rc b/emu/Nt/nt.rc
new file mode 100644
index 00000000..dd31bc0b
--- /dev/null
+++ b/emu/Nt/nt.rc
@@ -0,0 +1 @@
+100 ICON inferno.ico
diff --git a/emu/Nt/os.c b/emu/Nt/os.c
new file mode 100644
index 00000000..023d47c3
--- /dev/null
+++ b/emu/Nt/os.c
@@ -0,0 +1,875 @@
+#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"
+
+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 int sleepers = 0;
+
+ wchar_t *widen(char *s);
+ char *narrowen(wchar_t *ws);
+ int widebytes(wchar_t *ws);
+ int runeslen(Rune*);
+ Rune* runesdup(Rune*);
+ Rune* utftorunes(Rune*, char*, int);
+ char* runestoutf(char*, Rune*, int);
+ int runescmp(Rune*, Rune*);
+
+_declspec(thread) Proc *up;
+
+HANDLE ntfd2h(int);
+int nth2fd(HANDLE);
+void termrestore(void);
+char *hosttype = "Nt";
+char *cputype = "386";
+
+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);
+}
+
+static ulong erendezvous(void*, ulong);
+
+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;
+}
+
+int
+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){
+ print("out of kernel processes\n");
+ return -1;
+ }
+
+ 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){
+ pfree(p);
+ print("ran out of kernel processes\n");
+ return -1;
+ }
+ return p->pid;
+}
+
+#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, "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 void
+termset(void)
+{
+ DWORD flag;
+
+ if(conh != INVALID_HANDLE_VALUE)
+ return;
+ conh = GetStdHandle(STD_OUTPUT_HANDLE);
+ kbdh = GetStdHandle(STD_INPUT_HANDLE);
+
+ // 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);
+}
+
+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;
+
+static 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)
+ return 0;
+ 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){
+ if(conh == INVALID_HANDLE_VALUE){
+ termset();
+ if(conh == INVALID_HANDLE_VALUE)
+ return -1;
+ }
+ if(!WriteFile(conh, 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;
+}
+
+long
+time(long *tp)
+{
+ SYSTEMTIME tm;
+ long t;
+
+ GetSystemTime(&tm);
+ t = tm2sec(&tm);
+ if(tp != nil)
+ *tp = t;
+ return t;
+}
+
+/*
+ * 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)
+{
+ 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;
+}
+
+wchar_t *
+widen(char *s)
+{
+ int n;
+ wchar_t *ws;
+
+ n = utflen(s) + 1;
+ ws = smalloc(n*sizeof(wchar_t));
+ utftorunes(ws, s, n);
+ return ws;
+}
+
+
+char *
+narrowen(wchar_t *ws)
+{
+ char *s;
+ int n;
+
+ n = widebytes(ws);
+ s = smalloc(n);
+ runestoutf(s, ws, n);
+ return s;
+}
+
+
+int
+widebytes(wchar_t *ws)
+{
+ int n = 0;
+
+ while (*ws)
+ n += runelen(*ws++);
+ return n+1;
+}
diff --git a/emu/Nt/vlrt.c b/emu/Nt/vlrt.c
new file mode 100644
index 00000000..c9ef9c9a
--- /dev/null
+++ b/emu/Nt/vlrt.c
@@ -0,0 +1,764 @@
+#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
+{
+ union
+ {
+ struct
+ {
+ ulong lo;
+ ulong hi;
+ };
+ struct
+ {
+ ushort lols;
+ ushort loms;
+ ushort hils;
+ ushort hims;
+ };
+ };
+};
+
+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)
+{
+ union { double d; struct Vlong; } x;
+ ulong xhi, xlo, ylo, yhi;
+ int sh;
+
+ x.d = 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);
+}
diff --git a/emu/Nt/win.c b/emu/Nt/win.c
new file mode 100644
index 00000000..f9a6aa7d
--- /dev/null
+++ b/emu/Nt/win.c
@@ -0,0 +1,841 @@
+#define Unknown win_Unknown
+#include <windows.h>
+#undef Unknown
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include "keyboard.h"
+#include "cursor.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);
+
+/*
+ * defs for image types to overcome name conflicts
+ */
+typedef struct IPoint IPoint;
+typedef struct IRectangle IRectangle;
+
+struct IPoint
+{
+ LONG x;
+ LONG y;
+};
+
+struct IRectangle
+{
+ IPoint min;
+ IPoint max;
+};
+
+extern char* runestoutf(char*, Rune*, int);
+extern int bytesperline(IRectangle, 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;
+
+static 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;
+}
+
+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);
+}
+
+int
+col(int v, int n)
+{
+ int i, c;
+
+ c = 0;
+ for(i = 0; i < 8; i += n)
+ c |= v << (16-(n+i));
+ return c >> 8;
+}
+
+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;
+ return RGB24;
+}
+
+uchar*
+attachscreen(IRectangle *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(IRectangle 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;
+
+ 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_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:
+ x = LOWORD(lparam);
+ y = HIWORD(lparam);
+ 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, x, 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:
+ // no longer used?
+ //m.b = 128;
+ //m.modify = 1;
+ //mousetrack(128, 0, 0, 1);
+ 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;
+ /* drawend(); */
+ ExitThread(msg.wParam);
+ return 0;
+}
+
+void
+setpointer(int x, int y)
+{
+ POINT pt;
+
+ pt.x = x; pt.y = y;
+ ClientToScreen(window, (LPPOINT)&pt);
+ SetCursorPos(pt.x, pt.y);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+ HCURSOR nh, oh;
+ IRectangle 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;
+ /* passing IRectangle to Rectangle is safe */
+ 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)
+{
+ Rune *p;
+ int n;
+ char *q;
+
+ p = GlobalLock(h);
+ n = runenlen(p, runestrlen(p)+1);
+ q = malloc(n);
+ if(q != nil)
+ runestoutf(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, *e;
+ Rune *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(Rune));
+ if(h == NULL)
+ error(Enovmem);
+ rp = GlobalLock(h);
+ p = buf;
+ e = p+n;
+ while(p<e)
+ p += chartorune(rp++, p);
+ *rp = 0;
+ 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;
+}