diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /emu/Nt | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'emu/Nt')
| -rw-r--r-- | emu/Nt/audio.c | 810 | ||||
| -rw-r--r-- | emu/Nt/cmd.c | 245 | ||||
| -rw-r--r-- | emu/Nt/devarch.c | 414 | ||||
| -rw-r--r-- | emu/Nt/deveia.c | 672 | ||||
| -rw-r--r-- | emu/Nt/devfs.c | 2507 | ||||
| -rw-r--r-- | emu/Nt/emu | 108 | ||||
| -rw-r--r-- | emu/Nt/fp.c | 107 | ||||
| -rw-r--r-- | emu/Nt/ie | 104 | ||||
| -rw-r--r-- | emu/Nt/ie-os.c | 847 | ||||
| -rw-r--r-- | emu/Nt/ie-win.c | 223 | ||||
| -rw-r--r-- | emu/Nt/ieplugin.h | 75 | ||||
| -rw-r--r-- | emu/Nt/ipif.c | 407 | ||||
| -rw-r--r-- | emu/Nt/mkfile | 51 | ||||
| -rw-r--r-- | emu/Nt/nt.rc | 1 | ||||
| -rw-r--r-- | emu/Nt/os.c | 875 | ||||
| -rw-r--r-- | emu/Nt/vlrt.c | 764 | ||||
| -rw-r--r-- | emu/Nt/win.c | 841 |
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(®lock); + if(waserror()){ + qunlock(®lock); + return nil; + } + res = RegOpenKey(root, keyname, &key); + if(res != ERROR_SUCCESS) + regerr(res); + if(waserror()){ + RegCloseKey(key); + nexterror(); + } + n = 0; + res = RegQueryValueEx(key, name, NULL, &dtype, NULL, &n); + if(res != ERROR_SUCCESS) + regerr(res); + 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(®lock); + 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(¤t->r); + for(j = 0; j < nname; j++){ + if(!(nc->qid.type&QTDIR)){ + if(j==0) + error(Enotdir); + break; + } + if(!okelem(name[j], 0)){ + if(j == 0) + error(Efilename); + break; + } + p = fspath(current, name[j], path, FS(c)->spec); + if(FS(c)->checksec) { + *p = '\0'; + if(!sechasperm(path, XMODE, FS(c)->srv)){ + if(j == 0) + error(Eperm); + break; + } + *p = '\\'; + } + + if(strcmp(name[j], "..") == 0) { + if(fsisroot(c)) + nc->qid = rootqid; + else{ + ph = fswalkpath(current, "..", 1); + if(cnisroot(ph)){ + nc->qid = rootqid; + current = ph; + if(current != nil) + incref(¤t->r); + } + else { + fspath(ph, 0, path, FS(c)->spec); + if(!fsexist(path, &nc->qid)){ + cnameclose(ph); + if(j == 0) + error(Enonexist); + break; + } + } + next = fswalkpath(current, name[j], 1); + cnameclose(current); + current = next; + cnameclose(ph); + } + } + else{ + if(!fsexist(path, &nc->qid)){ + if(j == 0) + error(Enonexist); + break; + } + next = fswalkpath(current, name[j], 1); + cnameclose(current); + current = next; + } + wq->qid[wq->nqid++] = nc->qid; + } + poperror(); + if(wq->nqid < nname){ + cnameclose(current); + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else if(wq->clone){ + nc->aux = smalloc(sizeof(Fsinfo)); + nc->type = c->type; + FS(nc)->spec = FS(c)->spec; + FS(nc)->srv = FS(c)->srv; + FS(nc)->name = current; + FS(nc)->usesec = FS(c)->usesec; + FS(nc)->checksec = FS(c)->checksec; + } + return wq; +} + +Chan* +fsopen(Chan *c, int mode) +{ + HANDLE h; + int m, isdir, aflag, cflag; + char path[MAX_PATH]; + wchar_t *wpath; + + isdir = c->qid.type & QTDIR; + if(isdir && mode != OREAD) + error(Eperm); + fspath(FS(c)->name, 0, path, FS(c)->spec); + + if(FS(c)->checksec) { + switch(mode & (OTRUNC|3)) { + case OREAD: + seccheck(path, RMODE, FS(c)->srv); + break; + case OWRITE: + case OWRITE|OTRUNC: + seccheck(path, WMODE, FS(c)->srv); + break; + case ORDWR: + case ORDWR|OTRUNC: + case OREAD|OTRUNC: + seccheck(path, RMODE|WMODE, FS(c)->srv); + break; + case OEXEC: + seccheck(path, XMODE, FS(c)->srv); + break; + default: + error(Ebadarg); + } + } + + c->mode = openmode(mode); + if(isdir) + FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE); + else { + m = fsomode(mode & 3); + cflag = OPEN_EXISTING; + if(mode & OTRUNC) + cflag = TRUNCATE_EXISTING; + aflag = FILE_FLAG_RANDOM_ACCESS; + if(mode & ORCLOSE) + aflag |= FILE_FLAG_DELETE_ON_CLOSE; + if (winfileclash(path)) + error(Eexist); + wpath = widen(path); + h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, 0, cflag, aflag, 0); + free(wpath); + if(h == INVALID_HANDLE_VALUE) + oserror(); + FS(c)->fd = nth2fd(h); + } + + c->offset = 0; + FS(c)->offset = 0; + c->flag |= COPEN; + return c; +} + +void +fscreate(Chan *c, char *name, int mode, ulong perm) +{ + Stat st; + HANDLE h; + int m, aflag; + SECURITY_ATTRIBUTES sa; + SECURITY_DESCRIPTOR *sd; + BY_HANDLE_FILE_INFORMATION hi; + char *p, path[MAX_PATH], sdrock[SD_ROCK]; + wchar_t *wpath; + ACL *acl; + + if(!okelem(name, 1)) + error(Efilename); + + m = fsomode(mode & 3); + p = fspath(FS(c)->name, name, path, FS(c)->spec); + acl = (ACL*)smalloc(ACL_ROCK); + sd = nil; + if(FS(c)->usesec) { + *p = '\0'; + sd = secsd(path, sdrock); + *p = '\\'; + if(sd == nil){ + free(acl); + oserror(); + } + if(FS(c)->checksec && !secsdhasperm(sd, WMODE, FS(c)->srv) + || !secsdstat(sd, &st, FS(c)->srv)){ + if(sd != (void*)sdrock) + free(sd); + free(acl); + error(Eperm); + } + if(sd != (void*)sdrock) + free(sd); + if(perm & DMDIR) + st.mode = (perm & ~0777) | (st.mode & perm & 0777); + else + st.mode = (perm & ~0666) | (st.mode & perm & 0666); + st.owner = up->env->ui; + if(!isserver) + st.owner = fsuser; + sd = secmksd(sdrock, &st, acl, perm & DMDIR); + if(sd == nil){ + free(acl); + oserror(); + } + } + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = sd; + sa.bInheritHandle = 0; + + if(perm & DMDIR) { + if(mode != OREAD) { + free(acl); + error(Eisdir); + } + wpath = widen(path); + if(!CreateDirectory(wpath, &sa) || !fsexist(path, &c->qid)) { + free(wpath); + free(acl); + oserror(); + } + free(wpath); + FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE); + } + else { + aflag = 0; + if(mode & ORCLOSE) + aflag = FILE_FLAG_DELETE_ON_CLOSE; + if (winfileclash(path)) + error(Eexist); + wpath = widen(path); + h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, CREATE_ALWAYS, aflag, 0); + free(wpath); + if(h == INVALID_HANDLE_VALUE) { + free(acl); + oserror(); + } + FS(c)->fd = nth2fd(h); + c->qid.path = fsqidpath(path); + c->qid.type = 0; + c->qid.vers = 0; + if(GetFileInformationByHandle(h, &hi)) + c->qid.vers = unixtime(hi.ftLastWriteTime); + } + + c->mode = openmode(mode); + c->offset = 0; + FS(c)->offset = 0; + c->flag |= COPEN; + FS(c)->name = fswalkpath(FS(c)->name, name, 0); + free(acl); +} + +void +fsclose(Chan *c) +{ + HANDLE h; + + if(c->flag & COPEN){ + h = ntfd2h(FS(c)->fd); + if(h != INVALID_HANDLE_VALUE){ + if(c->qid.type & QTDIR) + FindClose(h); + else + CloseHandle(h); + } + } + if(c->flag & CRCLOSE){ + if(!waserror()){ + fsremove(c); + poperror(); + } + return; + } + fsfree(c); +} + +/* + * 64-bit seeks, using SetFilePointer because SetFilePointerEx + * is not supported by NT + */ +static void +fslseek(HANDLE h, vlong offset) +{ + LONG hi; + + if(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; +} |
