aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/drivers/win32
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/emulator/drivers/win32
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/emulator/drivers/win32')
-rw-r--r--erts/emulator/drivers/win32/mem_drv.c141
-rw-r--r--erts/emulator/drivers/win32/registry_drv.c535
-rw-r--r--erts/emulator/drivers/win32/ttsl_drv.c751
-rw-r--r--erts/emulator/drivers/win32/win_con.c2259
-rw-r--r--erts/emulator/drivers/win32/win_con.h39
-rw-r--r--erts/emulator/drivers/win32/win_efile.c1426
-rw-r--r--erts/emulator/drivers/win32/winsock_func.h102
7 files changed, 5253 insertions, 0 deletions
diff --git a/erts/emulator/drivers/win32/mem_drv.c b/erts/emulator/drivers/win32/mem_drv.c
new file mode 100644
index 0000000000..fa7c46eca8
--- /dev/null
+++ b/erts/emulator/drivers/win32/mem_drv.c
@@ -0,0 +1,141 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/* Purpose: Access to elib memory statistics */
+
+#include "sys.h"
+#include "erl_driver.h"
+#include "elib_stat.h"
+
+#define MAP_BUF_SIZE 1000 /* Max map size */
+#define HISTO_BUF_SIZE 100 /* Max histogram buckets */
+
+static ErlDrvData mem_start(ErlDrvPort, char*);
+static int mem_init(void);
+static void mem_stop(ErlDrvData);
+static void mem_command(ErlDrvData);
+
+ErlDrvEntry mem_driver_entry = {
+ mem_init,
+ mem_start,
+ mem_stop,
+ mem_command,
+ NULL,
+ NULL,
+ "mem_drv"
+};
+
+static int mem_init(void)
+{
+ return 0;
+}
+
+static ErlDrvData mem_start(ErlDrvPort port, char* buf)
+{
+ return (ErlDrvData)port;
+}
+
+static void mem_stop(ErlDrvData port)
+{
+}
+
+void putint32(p, v)
+byte* p; int v;
+{
+ p[0] = (v >> 24) & 0xff;
+ p[1] = (v >> 16) & 0xff;
+ p[2] = (v >> 8) & 0xff;
+ p[3] = (v) & 0xff;
+}
+
+int getint16(p)
+byte* p;
+{
+ return (p[0] << 8) | p[1];
+}
+
+/*
+** Command:
+** m L1 L0 -> a heap map of length L1*256 + L0 is returned
+** s -> X3 X2 X1 X0 Y3 Y2 Y1 Y0 Z3 Z2 Z1 Z0
+** X == Total heap size bytes
+** Y == Total free bytes
+** Z == Size of largest free block in bytes
+**
+** h L1 L0 B0 -> Generate a logarithm histogram base B with L buckets
+** l L1 L0 S0 -> Generate a linear histogram with step S with L buckets
+*/
+unsigned char outbuf[HISTO_BUF_SIZE*2*4];
+
+static void mem_command(ErlDrvData port, char* buf, int count)
+{
+ if ((count == 1) && buf[0] == 's') {
+ struct elib_stat info;
+ char v[3*4];
+
+ elib_stat(&info);
+
+ putint32(v, info.mem_total*4);
+ putint32(v+4, info.mem_free*4);
+ putint32(v+8, info.max_free*4);
+ driver_output((ErlDrvPort)port, v, 12);
+ return;
+ }
+ else if ((count == 3) && buf[0] == 'm') {
+ char w[MAP_BUF_SIZE];
+ int n = getint16(buf+1);
+
+ if (n > MAP_BUF_SIZE)
+ n = MAP_BUF_SIZE;
+ elib_heap_map(w, n);
+ driver_output((ErlDrvPort)port, w, n);
+ return;
+ }
+ else if ((count == 4) && (buf[0] == 'h' || buf[0] == 'l')) {
+ unsigned long vf[HISTO_BUF_SIZE];
+ unsigned long va[HISTO_BUF_SIZE];
+ int n = getint16(buf+1);
+ int base = (unsigned char) buf[3];
+
+ if (n >= HISTO_BUF_SIZE)
+ n = HISTO_BUF_SIZE;
+ if (buf[0] == 'l')
+ base = -base;
+ if (elib_histo(vf, va, n, base) < 0) {
+ driver_failure((ErlDrvPort)port, -1);
+ return;
+ }
+ else {
+ char* p = outbuf;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ putint32(p, vf[i]);
+ p += 4;
+ }
+ for (i = 0; i < n; i++) {
+ putint32(p, va[i]);
+ p += 4;
+ }
+ driver_output((ErlDrvPort)port, outbuf, n*8);
+ }
+ return;
+ }
+ driver_failure((ErlDrvPort)port, -1);
+}
+
diff --git a/erts/emulator/drivers/win32/registry_drv.c b/erts/emulator/drivers/win32/registry_drv.c
new file mode 100644
index 0000000000..05fd2ea55f
--- /dev/null
+++ b/erts/emulator/drivers/win32/registry_drv.c
@@ -0,0 +1,535 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Purpose: Interface to the registry API.
+ */
+
+#include <windows.h>
+#include "erl_driver.h"
+#include "sys.h"
+
+/*
+ * Commands recognised by this driver.
+ */
+
+#define CMD_GET_CURRENT 0
+#define CMD_OPEN_KEY 1
+#define CMD_CREATE_KEY 2
+#define CMD_GET_ALL_SUBKEYS 3
+#define CMD_GET_VALUE 4
+#define CMD_GET_ALL_VALUES 5
+#define CMD_SET_VALUE 6
+#define CMD_DELETE_KEY 7
+#define CMD_DELETE_VALUE 8
+
+/*
+ * Microsoft-specific function to map a WIN32 error code to a Posix errno.
+ */
+
+extern void _dosmaperr(DWORD);
+
+/*
+ * Information kept for a registry port (since there is no controlling
+ * Erlang process, all state must be kept here).
+ */
+
+typedef struct {
+ ErlDrvPort port; /* Port handle. */
+ REGSAM sam; /* Access for handles. */
+ HKEY hkey; /* Handle to open key. */
+ HKEY hkey_root; /* Root handle for current key. */
+ char* key; /* Name of key. */
+ DWORD key_size; /* Size of key. */
+ LPSTR name_buf; /* Buffer for names. */
+ DWORD name_buf_size; /* Size of name buffer. */
+ LPSTR value_buf; /* Buffer for values. */
+ DWORD value_buf_size; /* Size of value buffer. */
+} RegPort;
+
+
+/*
+ * Local functions.
+ */
+
+static void reply(RegPort* rp, LONG result);
+static BOOL fix_value_result(RegPort* rp, LONG result, DWORD type,
+ LPSTR name, DWORD nameSize, LPSTR value,
+ DWORD valueSize);
+static int key_reply(RegPort* rp, LPSTR name, DWORD nameSize);
+static int value_reply(RegPort* rp, DWORD type, LPSTR name, DWORD nameSize,
+ LPSTR value, DWORD valueSize);
+static int state_reply(RegPort* rp, HKEY root, LPSTR name, DWORD nameSize);
+static int maperror(DWORD error);
+/*
+ * Local variables.
+ */
+
+static int reg_init(void);
+static ErlDrvData reg_start(ErlDrvPort, char*);
+static void reg_stop(ErlDrvData);
+static void reg_from_erlang(ErlDrvData, char*, int);
+
+struct erl_drv_entry registry_driver_entry = {
+ reg_init,
+ reg_start,
+ reg_stop,
+ reg_from_erlang,
+ NULL,
+ NULL,
+ "registry__drv__",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static int
+reg_init(void)
+{
+ DEBUGF(("reg_init()\n"));
+ return 0;
+}
+
+static ErlDrvData
+reg_start(ErlDrvPort port, char* buf)
+{
+ RegPort* rp;
+ char* s;
+ REGSAM sam = KEY_READ;
+
+ if ((s = strchr(buf, ' ')) != NULL) {
+ while (isspace(*s))
+ s++;
+ while (*s != '\0') {
+ if (*s == 'r') {
+ sam |= KEY_READ;
+ } else if (*s == 'w') {
+ sam |= KEY_WRITE;
+ }
+ s++;
+ }
+ }
+
+ rp = driver_alloc(sizeof(RegPort));
+ if (rp == NULL) {
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ rp->port = port;
+ rp->hkey = rp->hkey_root = HKEY_CLASSES_ROOT;
+ rp->sam = sam;
+ rp->key = driver_alloc(1);
+ rp->key_size = 0;
+ rp->name_buf_size = 64;
+ rp->name_buf = driver_alloc(rp->name_buf_size);
+ rp->value_buf_size = 64;
+ rp->value_buf = driver_alloc(rp->value_buf_size);
+ return (ErlDrvData) rp;
+}
+
+static void
+reg_stop(ErlDrvData clientData)
+{
+ RegPort* rp = (RegPort *) clientData;
+
+ (void) RegCloseKey(rp->hkey);
+ driver_free(rp->name_buf);
+ driver_free(rp->value_buf);
+ driver_free(rp->key);
+ driver_free(rp);
+ /* return 1; */
+}
+
+static void
+reg_from_erlang(ErlDrvData clientData, char* buf, int count)
+{
+ RegPort* rp = (RegPort *) clientData;
+ int cmd;
+ HKEY hkey;
+ LONG result;
+ DWORD nameSize;
+ DWORD type; /* Type of data in buffer. */
+ DWORD valueSize; /* Size of value buffer. */
+
+ cmd = buf[0];
+ buf++, count--;
+ switch (cmd) {
+ case CMD_GET_CURRENT:
+ state_reply(rp, rp->hkey_root, rp->key, rp->key_size);
+ break;
+ case CMD_OPEN_KEY:
+ {
+ char* key;
+ HKEY newKey;
+
+ /*
+ * [HKEY(DWORD), KeyString(string)]
+ */
+
+ hkey = (HKEY) get_int32(buf+0);
+ rp->hkey_root = hkey;
+ key = buf+4;
+ result = RegOpenKeyEx(hkey, key, 0, rp->sam, &newKey);
+ if (result == ERROR_SUCCESS) {
+ RegCloseKey(rp->hkey);
+ rp->hkey = newKey;
+ driver_free(rp->key);
+ rp->key_size = strlen(key);
+ rp->key = driver_alloc(rp->key_size+1);
+ strcpy(rp->key, key);
+ }
+ reply(rp, result);
+ return;
+ }
+ break;
+ case CMD_CREATE_KEY:
+ {
+ char* key;
+ HKEY newKey;
+ DWORD disposition;
+
+ hkey = (HKEY) get_int32(buf+0);
+ rp->hkey_root = hkey;
+ key = buf+4;
+ result = RegCreateKeyEx(hkey, key, 0, "", 0, rp->sam, NULL,
+ &newKey, &disposition);
+ if (result == ERROR_SUCCESS) {
+ RegCloseKey(rp->hkey);
+ rp->hkey = newKey;
+ driver_free(rp->key);
+ rp->key_size = strlen(key);
+ rp->key = driver_alloc(rp->key_size+1);
+ strcpy(rp->key, key);
+ }
+ reply(rp, result);
+ return;
+ }
+ break;
+ case CMD_GET_ALL_SUBKEYS:
+ {
+ int i;
+
+ i = 0;
+ for (;;) {
+ nameSize = rp->name_buf_size;
+ result = RegEnumKeyEx(rp->hkey, i, rp->name_buf, &nameSize,
+ NULL, NULL, NULL, NULL);
+ if (result == ERROR_MORE_DATA) {
+ rp->name_buf_size *= 2;
+ rp->name_buf = driver_realloc(rp->name_buf,
+ rp->name_buf_size);
+ continue;
+ } else if (result == ERROR_NO_MORE_ITEMS) {
+ reply(rp, ERROR_SUCCESS);
+ return;
+ } else if (result != ERROR_SUCCESS) {
+ reply(rp, result);
+ return;
+ }
+ key_reply(rp, rp->name_buf, nameSize);
+ i++;
+ }
+ }
+ break;
+ case CMD_GET_VALUE:
+ do {
+ valueSize = rp->value_buf_size;
+ result = RegQueryValueEx(rp->hkey, buf, NULL, &type,
+ rp->value_buf, &valueSize);
+ } while (!fix_value_result(rp, result, type, buf, strlen(buf),
+ rp->value_buf, valueSize));
+ break;
+ case CMD_GET_ALL_VALUES:
+ {
+ int i;
+
+ i = 0;
+ for (;;) {
+ nameSize = rp->name_buf_size;
+ valueSize = rp->value_buf_size;
+ result = RegEnumValue(rp->hkey, i, rp->name_buf, &nameSize,
+ NULL, &type, rp->value_buf, &valueSize);
+ if (result == ERROR_NO_MORE_ITEMS) {
+ reply(rp, ERROR_SUCCESS);
+ return;
+ }
+ if (fix_value_result(rp, result, type, rp->name_buf, nameSize,
+ rp->value_buf, valueSize)) {
+ i++;
+ }
+ }
+ }
+ break;
+ case CMD_SET_VALUE:
+ {
+ LPSTR name;
+ DWORD dword;
+
+ /*
+ * [Type(DWORD), Name(string), Value(bytes)]
+ */
+
+ type = get_int32(buf);
+ buf += 4;
+ count -= 4;
+ name = buf;
+ nameSize = strlen(buf) + 1;
+ buf += nameSize;
+ count -= nameSize;
+ if (type == REG_DWORD) {
+ /*
+ * Must pass a pointer to a DWORD in host byte order.
+ */
+ dword = get_int32(buf);
+ buf = (char *) &dword;
+ ASSERT(count == 4);
+ }
+ result = RegSetValueEx(rp->hkey, name, 0, type, buf, count);
+ reply(rp, result);
+ }
+ break;
+ case CMD_DELETE_KEY:
+ result = RegDeleteKey(rp->hkey, NULL);
+ reply(rp, result);
+ break;
+ case CMD_DELETE_VALUE:
+ result = RegDeleteValue(rp->hkey, buf);
+ reply(rp, result);
+ break;
+ }
+ /* return 1; */
+}
+
+static BOOL
+fix_value_result(RegPort* rp, LONG result, DWORD type,
+ LPSTR name, DWORD nameSize, LPSTR value, DWORD valueSize)
+{
+ if (result == ERROR_MORE_DATA) {
+ DWORD max_name1;
+ DWORD max_name2;
+ DWORD max_value;
+ int ok;
+
+ ok = RegQueryInfoKey(rp->hkey, NULL, NULL, NULL,
+ NULL, &max_name1, NULL, NULL, &max_name2,
+ &max_value, NULL, NULL);
+#ifdef DEBUG
+ if (ok != ERROR_SUCCESS) {
+ char buff[256];
+ sprintf(buff,"Failure in registry_drv line %d, error = %d",
+ __LINE__, GetLastError());
+ MessageBox(NULL, buff, "Internal error", MB_OK);
+ ASSERT(ok == ERROR_SUCCESS);
+ }
+#endif
+ rp->name_buf_size = (max_name1 > max_name2 ? max_name1 : max_name2)
+ + 1;
+ rp->value_buf_size = max_value + 1;
+ rp->name_buf = driver_realloc(rp->name_buf, rp->name_buf_size);
+ rp->value_buf = driver_realloc(rp->value_buf, rp->value_buf_size);
+ return FALSE;
+ } else if (result != ERROR_SUCCESS) {
+ reply(rp, result);
+ return TRUE;
+ }
+
+ /*
+ * Do some data conversion which is easier to do here
+ * than in Erlang.
+ */
+
+ switch (type) {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ valueSize--; /* No reason to send the '\0' to Erlang. */
+ break;
+ case REG_DWORD_LITTLE_ENDIAN:
+ case REG_DWORD_BIG_ENDIAN:
+ /*
+ * The value is a DWORD stored in host byte order.
+ * We must retrieve it and store it in network byte order.
+ */
+ {
+ DWORD dword = * ((DWORD *) value);
+ put_int32(dword, value);
+ type = REG_DWORD; /* Simplify life for Erlang code. */
+ break;
+ }
+ }
+
+ return value_reply(rp, type, name, nameSize, value, valueSize);
+}
+
+/*
+ * Sends one of the following replies back to Erlang,
+ * depending on result:
+ *
+ * [$e|Posix error(string)] Error
+ * [$o] Ok
+ */
+
+static void
+reply(RegPort* rp, LONG result)
+{
+ char sbuf[256];
+
+ if (result == ERROR_SUCCESS) {
+ sbuf[0] = 'o';
+ driver_output(rp->port, sbuf, 1);
+ } else {
+ char* s;
+ char* t;
+ int err;
+
+ sbuf[0] = 'e';
+ err = maperror(result);
+ for (s = erl_errno_id(err), t = sbuf+1; *s; s++, t++) {
+ *t = tolower(*s);
+ }
+ driver_output(rp->port, sbuf, t-sbuf);
+ }
+ /* return 1; */
+}
+
+/*
+ * Sends a key to Erlang:
+ *
+ * [$k, Keyname(string)]
+ */
+
+static int
+key_reply(RegPort* rp, /* Pointer to port structure. */
+ LPSTR name, /* Pointer to name. */
+ DWORD nameSize) /* Length of name. */
+{
+ char sbuf[512];
+ char* s = sbuf;
+ int needed = 1+nameSize;
+
+ if (sizeof sbuf < needed) {
+ s = driver_alloc(needed);
+ }
+
+ s[0] = 'k';
+ memcpy(s+1, name, nameSize);
+ driver_output(rp->port, s, needed);
+
+ if (s != sbuf) {
+ driver_free(s);
+ }
+ return 1;
+}
+
+/*
+ * Sends a value to Erlang:
+ *
+ * [$v, Type(DWORD), Valuename(string), 0, Value(bytes)]
+ */
+
+static int
+value_reply(RegPort* rp, /* Pointer to port structure. */
+ DWORD type, /* Type of value */
+ LPSTR name, /* Pointer to name. */
+ DWORD nameSize, /* Length of name. */
+ LPSTR value, /* Pointer to value. */
+ DWORD valueSize) /* Size of value. */
+{
+ char sbuf[512];
+ char* s = sbuf;
+ int needed = 1+4+nameSize+1+valueSize;
+ int i;
+
+ if (sizeof sbuf < needed) {
+ s = driver_alloc(needed);
+ }
+
+ s[0] = 'v';
+ i = 1;
+ put_int32(type, s+i);
+ i += 4;
+ memcpy(s+i, name, nameSize);
+ i += nameSize;
+ s[i++] = '\0';
+ memcpy(s+i, value, valueSize);
+ ASSERT(i+valueSize == needed);
+ driver_output(rp->port, s, needed);
+
+ if (s != sbuf) {
+ driver_free(s);
+ }
+ return 1;
+}
+
+/*
+ * Sends a key to Erlang:
+ *
+ * [$s, HKEY(DWORD), Keyname(string)] State
+ */
+
+static int
+state_reply(RegPort* rp, /* Pointer to port structure. */
+ HKEY root, /* Handle to root key for this key. */
+ LPSTR name, /* Pointer to name. */
+ DWORD nameSize) /* Length of name. */
+{
+ char sbuf[512];
+ char* s = sbuf;
+ int needed = 1+4+nameSize;
+ int i;
+
+ if (sizeof sbuf < needed) {
+ s = driver_alloc(needed);
+ }
+
+ s[0] = 's';
+ i = 1;
+ put_int32((DWORD) root, s+i);
+ i += 4;
+ memcpy(s+i, name, nameSize);
+ ASSERT(i+nameSize == needed);
+ driver_output(rp->port, s, needed);
+
+ if (s != sbuf) {
+ driver_free(s);
+ }
+ return 1;
+}
+
+static int
+maperror(DWORD error)
+{
+ DEBUGF(("Mapping %d\n", error));
+ switch (error) {
+ case ERROR_BADDB:
+ case ERROR_BADKEY:
+ case ERROR_CANTOPEN:
+ case ERROR_CANTWRITE:
+ case ERROR_REGISTRY_RECOVERED:
+ case ERROR_REGISTRY_CORRUPT:
+ case ERROR_REGISTRY_IO_FAILED:
+ case ERROR_NOT_REGISTRY_FILE:
+ return EIO;
+ case ERROR_KEY_DELETED:
+ return EINVAL;
+ default:
+ _dosmaperr(error);
+ return errno;
+ }
+}
diff --git a/erts/emulator/drivers/win32/ttsl_drv.c b/erts/emulator/drivers/win32/ttsl_drv.c
new file mode 100644
index 0000000000..fd88dafd34
--- /dev/null
+++ b/erts/emulator/drivers/win32/ttsl_drv.c
@@ -0,0 +1,751 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Tty driver that reads one character at the time and provides a
+ * smart line for output.
+ */
+
+#include "sys.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+#include "erl_driver.h"
+#include "win_con.h"
+
+#define TRUE 1
+#define FALSE 0
+
+static int cols; /* Number of columns available. */
+static int rows; /* Number of rows available. */
+
+/* The various opcodes. */
+#define OP_PUTC 0
+#define OP_MOVE 1
+#define OP_INSC 2
+#define OP_DELC 3
+#define OP_BEEP 4
+
+/* Control op */
+#define CTRL_OP_GET_WINSIZE 100
+#define CTRL_OP_GET_UNICODE_STATE 101
+#define CTRL_OP_SET_UNICODE_STATE 102
+
+static int lbuf_size = BUFSIZ;
+Uint32 *lbuf; /* The current line buffer */
+int llen; /* The current line length */
+int lpos; /* The current "cursor position" in the line buffer */
+
+/*
+ * Tags used in line buffer to show that these bytes represent special characters,
+ * Max unicode is 0x0010ffff, so we have lots of place for meta tags...
+ */
+#define CONTROL_TAG 0x10000000U /* Control character, value in first position */
+#define ESCAPED_TAG 0x01000000U /* Escaped character, value in first position */
+#define TAG_MASK 0xFF000000U
+
+#define MAXSIZE (1 << 16)
+
+#define ISPRINT(c) (isprint(c) || (128+32 <= (c) && (c) < 256))
+
+#define DEBUGLOG(X) /* nothing */
+
+/*
+ * XXX These are used by win_con.c (for command history).
+ * Should be cleaned up.
+ */
+
+
+#define NL '\n'
+
+/* Main interface functions. */
+static int ttysl_init(void);
+static ErlDrvData ttysl_start(ErlDrvPort, char*);
+static void ttysl_stop(ErlDrvData);
+static int ttysl_control(ErlDrvData, unsigned int, char *, int, char **, int);
+static void ttysl_from_erlang(ErlDrvData, char*, int);
+static void ttysl_from_tty(ErlDrvData, ErlDrvEvent);
+static Sint16 get_sint16(char *s);
+
+static ErlDrvPort ttysl_port;
+
+extern ErlDrvEvent console_input_event;
+extern HANDLE console_thread;
+
+static HANDLE ttysl_in = INVALID_HANDLE_VALUE; /* Handle for console input. */
+static HANDLE ttysl_out = INVALID_HANDLE_VALUE; /* Handle for console output */
+
+/* Functions that work on the line buffer. */
+static int start_lbuf();
+static int stop_lbuf();
+static int put_chars();
+static int move_rel();
+static int ins_chars();
+static int del_chars();
+static int step_over_chars(int n);
+static int insert_buf();
+static int write_buf();
+static void move_cursor(int, int);
+
+/* Define the driver table entry. */
+struct erl_drv_entry ttsl_driver_entry = {
+ ttysl_init,
+ ttysl_start,
+ ttysl_stop,
+ ttysl_from_erlang,
+ ttysl_from_tty,
+ NULL,
+ "tty_sl",
+ NULL,
+ NULL,
+ ttysl_control,
+ NULL
+};
+
+static int utf8_mode = 0;
+
+static int ttysl_init()
+{
+ lbuf = NULL; /* For line buffer handling */
+ ttysl_port = (ErlDrvPort)-1;
+ return 0;
+}
+
+static ErlDrvData ttysl_start(ErlDrvPort port, char* buf)
+{
+ if ((int)ttysl_port != -1 || console_thread == NULL) {
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ start_lbuf();
+ utf8_mode = 1;
+ driver_select(port, console_input_event, ERL_DRV_READ, 1);
+ ttysl_port = port;
+ return (ErlDrvData)ttysl_port;/* Nothing important to return */
+}
+
+#define DEF_HEIGHT 24
+#define DEF_WIDTH 80
+
+static void ttysl_get_window_size(Uint32 *width, Uint32 *height)
+{
+ *width = ConGetColumns();
+ *height = ConGetRows();
+}
+
+
+static int ttysl_control(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, int len,
+ char **rbuf, int rlen)
+{
+ char resbuff[2*sizeof(Uint32)];
+ int res_size;
+ switch (command) {
+ case CTRL_OP_GET_WINSIZE:
+ {
+ Uint32 w,h;
+ ttysl_get_window_size(&w,&h);
+ memcpy(resbuff,&w,sizeof(Uint32));
+ memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32));
+ res_size = 2*sizeof(Uint32);
+ }
+ break;
+ case CTRL_OP_GET_UNICODE_STATE:
+ *resbuff = (utf8_mode) ? 1 : 0;
+ res_size = 1;
+ break;
+ case CTRL_OP_SET_UNICODE_STATE:
+ if (len > 0) {
+ int m = (int) *buf;
+ *resbuff = (utf8_mode) ? 1 : 0;
+ res_size = 1;
+ utf8_mode = (m) ? 1 : 0;
+ } else {
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+ if (rlen < res_size) {
+ *rbuf = driver_alloc(res_size);
+ }
+ memcpy(*rbuf,resbuff,res_size);
+ return res_size;
+}
+
+
+static void ttysl_stop(ErlDrvData ttysl_data)
+{
+ if ((int)ttysl_port != -1) {
+ driver_select(ttysl_port, console_input_event, ERL_DRV_READ, 0);
+ }
+
+ ttysl_in = ttysl_out = INVALID_HANDLE_VALUE;
+ stop_lbuf();
+ ttysl_port = (ErlDrvPort)-1;
+}
+
+static int put_utf8(int ch, byte *target, int sz, int *pos)
+{
+ Uint x = (Uint) ch;
+ if (x < 0x80) {
+ if (*pos >= sz) {
+ return -1;
+ }
+ target[(*pos)++] = (byte) x;
+ }
+ else if (x < 0x800) {
+ if (((*pos) + 1) >= sz) {
+ return -1;
+ }
+ target[(*pos)++] = (((byte) (x >> 6)) |
+ ((byte) 0xC0));
+ target[(*pos)++] = (((byte) (x & 0x3F)) |
+ ((byte) 0x80));
+ } else if (x < 0x10000) {
+ if ((x >= 0xD800 && x <= 0xDFFF) ||
+ (x == 0xFFFE) ||
+ (x == 0xFFFF)) { /* Invalid unicode range */
+ return -1;
+ }
+ if (((*pos) + 2) >= sz) {
+ return -1;
+ }
+
+ target[(*pos)++] = (((byte) (x >> 12)) |
+ ((byte) 0xE0));
+ target[(*pos)++] = ((((byte) (x >> 6)) & 0x3F) |
+ ((byte) 0x80));
+ target[(*pos)++] = (((byte) (x & 0x3F)) |
+ ((byte) 0x80));
+ } else if (x < 0x110000) { /* Standard imposed max */
+ if (((*pos) + 3) >= sz) {
+ return -1;
+ }
+ target[(*pos)++] = (((byte) (x >> 18)) |
+ ((byte) 0xF0));
+ target[(*pos)++] = ((((byte) (x >> 12)) & 0x3F) |
+ ((byte) 0x80));
+ target[(*pos)++] = ((((byte) (x >> 6)) & 0x3F) |
+ ((byte) 0x80));
+ target[(*pos)++] = (((byte) (x & 0x3F)) |
+ ((byte) 0x80));
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+
+static int pick_utf8(byte *s, int sz, int *pos)
+{
+ int size = sz - (*pos);
+ byte *source;
+ Uint unipoint;
+
+ if (size > 0) {
+ source = s + (*pos);
+ if (((*source) & ((byte) 0x80)) == 0) {
+ unipoint = (int) *source;
+ ++(*pos);
+ return (int) unipoint;
+ } else if (((*source) & ((byte) 0xE0)) == 0xC0) {
+ if (size < 2) {
+ return -2;
+ }
+ if (((source[1] & ((byte) 0xC0)) != 0x80) ||
+ ((*source) < 0xC2) /* overlong */) {
+ return -1;
+ }
+ (*pos) += 2;
+ unipoint =
+ (((Uint) ((*source) & ((byte) 0x1F))) << 6) |
+ ((Uint) (source[1] & ((byte) 0x3F)));
+ return (int) unipoint;
+ } else if (((*source) & ((byte) 0xF0)) == 0xE0) {
+ if (size < 3) {
+ return -2;
+ }
+ if (((source[1] & ((byte) 0xC0)) != 0x80) ||
+ ((source[2] & ((byte) 0xC0)) != 0x80) ||
+ (((*source) == 0xE0) && (source[1] < 0xA0)) /* overlong */ ) {
+ return -1;
+ }
+ if ((((*source) & ((byte) 0xF)) == 0xD) &&
+ ((source[1] & 0x20) != 0)) {
+ return -1;
+ }
+ if (((*source) == 0xEF) && (source[1] == 0xBF) &&
+ ((source[2] == 0xBE) || (source[2] == 0xBF))) {
+ return -1;
+ }
+ (*pos) += 3;
+ unipoint =
+ (((Uint) ((*source) & ((byte) 0xF))) << 12) |
+ (((Uint) (source[1] & ((byte) 0x3F))) << 6) |
+ ((Uint) (source[2] & ((byte) 0x3F)));
+ return (int) unipoint;
+ } else if (((*source) & ((byte) 0xF8)) == 0xF0) {
+ if (size < 4) {
+ return -2 ;
+ }
+ if (((source[1] & ((byte) 0xC0)) != 0x80) ||
+ ((source[2] & ((byte) 0xC0)) != 0x80) ||
+ ((source[3] & ((byte) 0xC0)) != 0x80) ||
+ (((*source) == 0xF0) && (source[1] < 0x90)) /* overlong */) {
+ return -1;
+ }
+ if ((((*source) & ((byte)0x7)) > 0x4U) ||
+ ((((*source) & ((byte)0x7)) == 0x4U) &&
+ ((source[1] & ((byte)0x3F)) > 0xFU))) {
+ return -1;
+ }
+ (*pos) += 4;
+ unipoint =
+ (((Uint) ((*source) & ((byte) 0x7))) << 18) |
+ (((Uint) (source[1] & ((byte) 0x3F))) << 12) |
+ (((Uint) (source[2] & ((byte) 0x3F))) << 6) |
+ ((Uint) (source[3] & ((byte) 0x3F)));
+ return (int) unipoint;
+ } else {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+}
+
+static int octal_or_hex_positions(Uint c)
+{
+ int x = 0;
+ Uint ch = c;
+ if (!ch) {
+ return 1;
+ }
+ while(ch) {
+ ++x;
+ ch >>= 3;
+ }
+ if (x <= 3) {
+ return 3;
+ }
+ /* \x{H ...} format when larger than \777 */
+ x = 0;
+ ch = c;
+ while(ch) {
+ ++x;
+ ch >>= 4;
+ }
+ return x+3;
+}
+
+static void octal_or_hex_format(Uint ch, byte *buf, int *pos)
+{
+ static byte hex_chars[] = { '0','1','2','3','4','5','6','7','8','9',
+ 'A','B','C','D','E','F'};
+ int num = octal_or_hex_positions(ch);
+ if (num != 3) {
+ buf[(*pos)++] = 'x';
+ buf[(*pos)++] = '{';
+ num -= 3;
+ while(num--) {
+ buf[(*pos)++] = hex_chars[((ch >> (4*num)) & 0xFU)];
+ }
+ buf[(*pos)++] = '}';
+ } else {
+ while(num--) {
+ buf[(*pos)++] = ((byte) ((ch >> (3*num)) & 0x7U) + '0');
+ }
+ }
+}
+
+/*
+ * Check that there is enough room in all buffers to copy all pad chars
+ * and stiff we need If not, realloc lbuf.
+ */
+static int check_buf_size(byte *s, int n)
+{
+ int pos = 0;
+ int ch;
+ int size = 10;
+
+ while(pos < n) {
+ /* Indata is always UTF-8 */
+ if ((ch = pick_utf8(s,n,&pos)) < 0) {
+ /* XXX temporary allow invalid chars */
+ ch = (int) s[pos];
+ DEBUGLOG(("Invalid UTF8:%d",ch));
+ ++pos;
+ }
+ if (utf8_mode) { /* That is, terminal is UTF8 compliant */
+ if (ch >= 128 || isprint(ch)) {
+ DEBUGLOG(("Printable(UTF-8:%d):%d",(pos - opos),ch));
+ size++; /* Buffer contains wide characters... */
+ } else if (ch == '\t') {
+ size += 8;
+ } else {
+ DEBUGLOG(("Magic(UTF-8:%d):%d",(pos - opos),ch));
+ size += 2;
+ }
+ } else {
+ if (ch <= 255 && isprint(ch)) {
+ DEBUGLOG(("Printable:%d",ch));
+ size++;
+ } else if (ch == '\t')
+ size += 8;
+ else if (ch >= 128) {
+ DEBUGLOG(("Non printable:%d",ch));
+ size += (octal_or_hex_positions(ch) + 1);
+ }
+ else {
+ DEBUGLOG(("Magic:%d",ch));
+ size += 2;
+ }
+ }
+ }
+
+ if (size + lpos >= lbuf_size) {
+
+ lbuf_size = size + lpos + BUFSIZ;
+ if ((lbuf = driver_realloc(lbuf, lbuf_size * sizeof(Uint32))) == NULL) {
+ driver_failure(ttysl_port, -1);
+ return(0);
+ }
+ }
+ return(1);
+}
+
+
+static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, int count)
+{
+ if (lpos > MAXSIZE)
+ put_chars((byte*)"\n", 1);
+
+ switch (buf[0]) {
+ case OP_PUTC:
+ DEBUGLOG(("OP: Putc(%d)",count-1));
+ if (check_buf_size((byte*)buf+1, count-1) == 0)
+ return;
+ put_chars((byte*)buf+1, count-1);
+ break;
+ case OP_MOVE:
+ move_rel(get_sint16(buf+1));
+ break;
+ case OP_INSC:
+ if (check_buf_size((byte*)buf+1, count-1) == 0)
+ return;
+ ins_chars((byte*)buf+1, count-1);
+ break;
+ case OP_DELC:
+ del_chars(get_sint16(buf+1));
+ break;
+ case OP_BEEP:
+ ConBeep();
+ break;
+ default:
+ /* Unknown op, just ignore. */
+ break;
+ }
+ return;
+}
+
+extern int read_inbuf(char *data, int n);
+
+static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd)
+{
+ Uint32 inbuf[64];
+ byte t[1024];
+ int i,pos,tpos;
+
+ i = ConReadInput(inbuf,1);
+
+ pos = 0;
+ tpos = 0;
+
+ while (pos < i) {
+ while (tpos < 1020 && pos < i) { /* Max 4 bytes for UTF8 */
+ put_utf8((int) inbuf[pos++], t, 1024, &tpos);
+ }
+ driver_output(ttysl_port, (char *) t, tpos);
+ tpos = 0;
+ }
+}
+
+/*
+ * Gets signed 16 bit integer from binary buffer.
+ */
+static Sint16
+get_sint16(char *s)
+{
+ return ((*s << 8) | ((byte*)s)[1]);
+}
+
+
+static int start_lbuf(void)
+{
+ if (!lbuf && !(lbuf = ( Uint32*) driver_alloc(lbuf_size * sizeof(Uint32))))
+ return FALSE;
+ llen = 0;
+ lpos = 0;
+ return TRUE;
+}
+
+static int stop_lbuf(void)
+{
+ if (lbuf) {
+ driver_free(lbuf);
+ lbuf = NULL;
+ }
+ llen = 0; /* To avoid access error in win_con:AddToCmdHistory during exit*/
+ return TRUE;
+}
+
+/* Put l bytes (in UTF8) from s into the buffer and output them. */
+static int put_chars(byte *s, int l)
+{
+ int n;
+
+ n = insert_buf(s, l);
+ if (n > 0)
+ write_buf(lbuf + lpos - n, n);
+ if (lpos > llen)
+ llen = lpos;
+ return TRUE;
+}
+
+/*
+ * Move the current postition forwards or backwards within the current
+ * line. We know about padding.
+ */
+static int move_rel(int n)
+{
+ int npos; /* The new position */
+
+ /* Step forwards or backwards over the buffer. */
+ npos = step_over_chars(n);
+
+ /* Calculate move, updates pointers and move the cursor. */
+ move_cursor(lpos, npos);
+ lpos = npos;
+ return TRUE;
+}
+
+/* Insert characters into the buffer at the current position. */
+static int ins_chars(byte *s, int l)
+{
+ int n, tl;
+ Uint32 *tbuf = NULL; /* Suppress warning about use-before-set */
+
+ /* Move tail of buffer to make space. */
+ if ((tl = llen - lpos) > 0) {
+ if ((tbuf = driver_alloc(tl * sizeof(Uint32))) == NULL)
+ return FALSE;
+ memcpy(tbuf, lbuf + lpos, tl * sizeof(Uint32));
+ }
+ n = insert_buf(s, l);
+ if (tl > 0) {
+ memcpy(lbuf + lpos, tbuf, tl * sizeof(Uint32));
+ driver_free(tbuf);
+ }
+ llen += n;
+ write_buf(lbuf + (lpos - n), llen - (lpos - n));
+ move_cursor(llen, lpos);
+ return TRUE;
+}
+
+/*
+ * Delete characters in the buffer. Can delete characters before (n < 0)
+ * and after (n > 0) the current position. Cursor left at beginning of
+ * deleted block.
+ */
+static int del_chars(int n)
+{
+ int i, l, r;
+ int pos;
+
+ /*update_cols();*/
+
+ /* Step forward or backwards over n logical characters. */
+ pos = step_over_chars(n);
+
+ if (pos > lpos) {
+ l = pos - lpos; /* Buffer characters to delete */
+ r = llen - lpos - l; /* Characters after deleted */
+ /* Fix up buffer and buffer pointers. */
+ if (r > 0)
+ memcpy(lbuf + lpos, lbuf + pos, r * sizeof(Uint32));
+ llen -= l;
+ /* Write out characters after, blank the tail and jump back to lpos. */
+ write_buf(lbuf + lpos, r);
+ for (i = l ; i > 0; --i)
+ ConPutChar(' ');
+ move_cursor(llen + l, lpos);
+ }
+ else if (pos < lpos) {
+ l = lpos - pos; /* Buffer characters */
+ r = llen - lpos; /* Characters after deleted */
+ move_cursor(lpos, lpos-l); /* Move back */
+ /* Fix up buffer and buffer pointers. */
+ if (r > 0)
+ memcpy(lbuf + pos, lbuf + lpos, r * sizeof(Uint32));
+ lpos -= l;
+ llen -= l;
+ /* Write out characters after, blank the tail and jump back to lpos. */
+ write_buf(lbuf + lpos, r);
+ for (i = l ; i > 0; --i)
+ ConPutChar(' ');
+ move_cursor(llen + l, lpos);
+ }
+ return TRUE;
+}
+
+
+/* Step over n logical characters, check for overflow. */
+static int step_over_chars(int n)
+{
+ Uint32 *c, *beg, *end;
+
+ beg = lbuf;
+ end = lbuf + llen;
+ c = lbuf + lpos;
+ for ( ; n > 0 && c < end; --n) {
+ c++;
+ while (c < end && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0))
+ c++;
+ }
+ for ( ; n < 0 && c > beg; n++) {
+ --c;
+ while (c > beg && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0))
+ --c;
+ }
+ return c - lbuf;
+}
+
+static int insert_buf(byte *s, int n)
+{
+ int pos = 0;
+ int buffpos = lpos;
+ int ch;
+
+ while (pos < n) {
+ if ((ch = pick_utf8(s,n,&pos)) < 0) {
+ /* XXX temporary allow invalid chars */
+ ch = (int) s[pos];
+ DEBUGLOG(("insert_buf: Invalid UTF8:%d",ch));
+ ++pos;
+ }
+ if ((utf8_mode && (ch >= 128 || isprint(ch))) || (ch <= 255 && isprint(ch))) {
+ DEBUGLOG(("insert_buf: Printable(UTF-8):%d",ch));
+ lbuf[lpos++] = (Uint32) ch;
+ } else if (ch >= 128) { /* not utf8 mode */
+ int nc = octal_or_hex_positions(ch);
+ lbuf[lpos++] = ((Uint32) ch) | ESCAPED_TAG;
+ while (nc--) {
+ lbuf[lpos++] = ESCAPED_TAG;
+ }
+ } else if (ch == '\t') {
+ do {
+ lbuf[lpos++] = (CONTROL_TAG | ((Uint32) ch));
+ ch = 0;
+ } while (lpos % 8);
+ } else if (ch == '\n' || ch == '\r') {
+ write_buf(lbuf + buffpos, lpos - buffpos);
+ ConPutChar('\r');
+ if (ch == '\n')
+ ConPutChar('\n');
+ if (llen > lpos) {
+ memcpy(lbuf, lbuf + lpos, llen - lpos);
+ }
+ llen -= lpos;
+ lpos = buffpos = 0;
+ } else {
+ DEBUGLOG(("insert_buf: Magic(UTF-8):%d",ch));
+ lbuf[lpos++] = ch | CONTROL_TAG;
+ lbuf[lpos++] = CONTROL_TAG;
+ }
+ }
+ return lpos - buffpos; /* characters "written" into
+ current buffer (may be less due to newline) */
+}
+static int write_buf(Uint32 *s, int n)
+{
+ int i;
+
+ /*update_cols();*/
+
+ while (n > 0) {
+ if (!(*s & TAG_MASK) ) {
+ ConPutChar(*s);
+ --n;
+ ++s;
+ }
+ else if (*s == (CONTROL_TAG | ((Uint32) '\t'))) {
+ ConPutChar(' ');
+ --n; s++;
+ while (n > 0 && *s == CONTROL_TAG) {
+ ConPutChar(' ');
+ --n; s++;
+ }
+ } else if (*s & CONTROL_TAG) {
+ ConPutChar('^');
+ ConPutChar((*s == 0177) ? '?' : *s | 0x40);
+ n -= 2;
+ s += 2;
+ } else if (*s & ESCAPED_TAG) {
+ Uint32 ch = *s & ~(TAG_MASK);
+ byte *octbuff;
+ byte octtmp[256];
+ int octbytes;
+ DEBUGLOG(("Escaped: %d", ch));
+ octbytes = octal_or_hex_positions(ch);
+ if (octbytes > 256) {
+ octbuff = driver_alloc(octbytes);
+ } else {
+ octbuff = octtmp;
+ }
+ octbytes = 0;
+ octal_or_hex_format(ch, octbuff, &octbytes);
+ DEBUGLOG(("octbytes: %d", octbytes));
+ ConPutChar('\\');
+ for (i = 0; i < octbytes; ++i) {
+ ConPutChar(octbuff[i]);
+ }
+ n -= octbytes+1;
+ s += octbytes+1;
+ if (octbuff != octtmp) {
+ driver_free(octbuff);
+ }
+ } else {
+ DEBUGLOG(("Very unexpected character %d",(int) *s));
+ ++n;
+ --s;
+ }
+ }
+ return TRUE;
+}
+
+
+static void
+move_cursor(int from, int to)
+{
+ ConSetCursor(from,to);
+}
diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c
new file mode 100644
index 0000000000..2202ca655f
--- /dev/null
+++ b/erts/emulator/drivers/win32/win_con.c
@@ -0,0 +1,2259 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#define UNICODE 1
+#define _UNICODE 1
+#include <tchar.h>
+#include <stdio.h>
+#include "sys.h"
+#include <windowsx.h>
+#include "resource.h"
+#include "erl_version.h"
+#include <commdlg.h>
+#include <commctrl.h>
+#include "erl_driver.h"
+#include "win_con.h"
+
+#define ALLOC(X) malloc(X)
+#define REALLOC(X,Y) realloc(X,Y)
+#define FREE(X) free(X)
+
+#ifndef STATE_SYSTEM_INVISIBLE
+/* Mingw problem with oleacc.h and WIN32_LEAN_AND_MEAN */
+#define STATE_SYSTEM_INVISIBLE 0x00008000
+#endif
+
+#define WM_CONTEXT (0x0401)
+#define WM_CONBEEP (0x0402)
+#define WM_SAVE_PREFS (0x0403)
+
+#define USER_KEY TEXT("Software\\Ericsson\\Erlang\\") TEXT(ERLANG_VERSION)
+
+#define FRAME_HEIGHT ((2*GetSystemMetrics(SM_CYEDGE))+(2*GetSystemMetrics(SM_CYFRAME))+GetSystemMetrics(SM_CYCAPTION))
+#define FRAME_WIDTH (2*GetSystemMetrics(SM_CXFRAME)+(2*GetSystemMetrics(SM_CXFRAME))+GetSystemMetrics(SM_CXVSCROLL))
+
+#define LINE_LENGTH canvasColumns
+#define COL(_l) ((_l) % LINE_LENGTH)
+#define LINE(_l) ((_l) / LINE_LENGTH)
+
+#ifdef UNICODE
+/*
+ * We use a character in the invalid unicode range
+ */
+#define SET_CURSOR (0xD8FF)
+#else
+/*
+ * XXX There is no escape to send a character 0x80. Fortunately,
+ * the ttsl driver currently replaces 0x80 with an octal sequence.
+ */
+#define SET_CURSOR (0x80)
+#endif
+
+#define SCAN_CODE_BREAK 0x46 /* scan code for Ctrl-Break */
+
+
+typedef struct ScreenLine_s {
+ struct ScreenLine_s* next;
+ struct ScreenLine_s* prev;
+ int width;
+#ifdef HARDDEBUG
+ int allocated;
+#endif
+ int newline; /* Ends with hard newline: 1, wrapped at end: 0 */
+ TCHAR *text;
+} ScreenLine_t;
+
+extern Uint32 *lbuf; /* The current line buffer */
+extern int llen; /* The current line length */
+extern int lpos;
+
+HANDLE console_input_event;
+HANDLE console_thread = NULL;
+
+#define DEF_CANVAS_COLUMNS 80
+#define DEF_CANVAS_ROWS 26
+
+#define BUFSIZE 4096
+#define MAXBUFSIZE 32768
+typedef struct {
+ TCHAR *data;
+ int size;
+ int wrPos;
+ int rdPos;
+} buffer_t;
+
+static buffer_t inbuf;
+static buffer_t outbuf;
+
+static CHOOSEFONT cf;
+
+static TCHAR szFrameClass[] = TEXT("FrameClass");
+static TCHAR szClientClass[] = TEXT("ClientClass");
+static HWND hFrameWnd;
+static HWND hClientWnd;
+static HWND hTBWnd;
+static HWND hComboWnd;
+static HANDLE console_input;
+static HANDLE console_output;
+static int cxChar,cyChar, cxCharMax;
+static int cxClient,cyClient;
+static int cyToolBar;
+static int iVscrollPos,iHscrollPos;
+static int iVscrollMax,iHscrollMax;
+static int nBufLines;
+static int cur_x;
+static int cur_y;
+static int canvasColumns = DEF_CANVAS_COLUMNS;
+static int canvasRows = DEF_CANVAS_ROWS;
+static ScreenLine_t *buffer_top,*buffer_bottom;
+static ScreenLine_t* cur_line;
+static POINT editBeg,editEnd;
+static BOOL fSelecting = FALSE;
+static BOOL fTextSelected = FALSE;
+static HKEY key;
+static BOOL has_key = FALSE;
+static LOGFONT logfont;
+static DWORD fgColor;
+static DWORD bkgColor;
+static FILE *logfile = NULL;
+static RECT winPos;
+static BOOL toolbarVisible;
+static BOOL destroyed = FALSE;
+
+static int lines_to_save = 1000; /* Maximum number of screen lines to save. */
+
+#define TITLE_BUF_SZ 256
+
+struct title_buf {
+ TCHAR *name;
+ TCHAR buf[TITLE_BUF_SZ];
+};
+
+static TCHAR *erlang_window_title = TEXT("Erlang");
+
+static unsigned __stdcall ConThreadInit(LPVOID param);
+static LRESULT CALLBACK ClientWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
+static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam);
+static ScreenLine_t *ConNewLine(void);
+static void DeleteTopLine(void);
+static void ensure_line_below(void);
+static ScreenLine_t *GetLineFromY(int y);
+static void LoadUserPreferences(void);
+static void SaveUserPreferences(void);
+static void set_scroll_info(HWND hwnd);
+static void ConCarriageFeed(int);
+static void ConScrollScreen(void);
+static BOOL ConChooseFont(HWND hwnd);
+static void ConFontInitialize(HWND hwnd);
+static void ConSetFont(HWND hwnd);
+static void ConChooseColor(HWND hwnd);
+static void DrawSelection(HWND hwnd, POINT pt1, POINT pt2);
+static void InvertSelectionArea(HWND hwnd);
+static void OnEditCopy(HWND hwnd);
+static void OnEditPaste(HWND hwnd);
+static void OnEditSelAll(HWND hwnd);
+static void GetFileName(HWND hwnd, TCHAR *pFile);
+static void OpenLogFile(HWND hwnd);
+static void CloseLogFile(HWND hwnd);
+static void LogFileWrite(TCHAR *buf, int n);
+static int write_inbuf(TCHAR *data, int n);
+static void init_buffers(void);
+static void AddToCmdHistory(void);
+static int write_outbuf(TCHAR *data, int num_chars);
+static void ConDrawText(HWND hwnd);
+static BOOL (WINAPI *ctrl_handler)(DWORD);
+static HWND InitToolBar(HWND hwndParent);
+static void window_title(struct title_buf *);
+static void free_window_title(struct title_buf *);
+static void Client_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
+
+#define CON_VPRINTF_BUF_INC_SIZE 1024
+
+static erts_dsprintf_buf_t *
+grow_con_vprintf_buf(erts_dsprintf_buf_t *dsbufp, size_t need)
+{
+ char *buf;
+ size_t size;
+
+ ASSERT(dsbufp);
+
+ if (!dsbufp->str) {
+ size = (((need + CON_VPRINTF_BUF_INC_SIZE - 1)
+ / CON_VPRINTF_BUF_INC_SIZE)
+ * CON_VPRINTF_BUF_INC_SIZE);
+ buf = (char *) ALLOC(size * sizeof(char));
+ }
+ else {
+ size_t free_size = dsbufp->size - dsbufp->str_len;
+
+ if (need <= free_size)
+ return dsbufp;
+
+ size = need - free_size + CON_VPRINTF_BUF_INC_SIZE;
+ size = (((size + CON_VPRINTF_BUF_INC_SIZE - 1)
+ / CON_VPRINTF_BUF_INC_SIZE)
+ * CON_VPRINTF_BUF_INC_SIZE);
+ size += dsbufp->size;
+ buf = (char *) REALLOC((void *) dsbufp->str,
+ size * sizeof(char));
+ }
+ if (!buf)
+ return NULL;
+ if (buf != dsbufp->str)
+ dsbufp->str = buf;
+ dsbufp->size = size;
+ return dsbufp;
+}
+
+static int con_vprintf(char *format, va_list arg_list)
+{
+ int res,i;
+ erts_dsprintf_buf_t dsbuf = ERTS_DSPRINTF_BUF_INITER(grow_con_vprintf_buf);
+ res = erts_vdsprintf(&dsbuf, format, arg_list);
+ if (res >= 0) {
+ TCHAR *tmp = ALLOC(dsbuf.str_len*sizeof(TCHAR));
+ for (i=0;i<dsbuf.str_len;++i) {
+ tmp[i] = dsbuf.str[i];
+ }
+ write_outbuf(tmp, dsbuf.str_len);
+ FREE(tmp);
+ }
+ if (dsbuf.str)
+ FREE((void *) dsbuf.str);
+ return res;
+}
+
+void
+ConInit(void)
+{
+ unsigned tid;
+
+ console_input = CreateSemaphore(NULL, 0, 1, NULL);
+ console_output = CreateSemaphore(NULL, 0, 1, NULL);
+ console_input_event = CreateManualEvent(FALSE);
+ console_thread = (HANDLE *) _beginthreadex(NULL, 0,
+ ConThreadInit,
+ 0, 0, &tid);
+
+ /* Make all erts_*printf on stdout and stderr use con_vprintf */
+ erts_printf_stdout_func = con_vprintf;
+ erts_printf_stderr_func = con_vprintf;
+}
+
+/*
+ ConNormalExit() is called from erl_exit() when the emulator
+ is stopping. If the exit has not been initiated by this
+ console thread (WM_DESTROY or ID_BREAK), the function must
+ invoke the console thread to save the user preferences.
+*/
+void
+ConNormalExit(void)
+{
+ if (!destroyed)
+ SendMessage(hFrameWnd, WM_SAVE_PREFS, 0L, 0L);
+}
+
+void
+ConWaitForExit(void)
+{
+ ConPrintf("\n\nAbnormal termination\n");
+ WaitForSingleObject(console_thread, INFINITE);
+}
+
+void ConSetCtrlHandler(BOOL (WINAPI *handler)(DWORD))
+{
+ ctrl_handler = handler;
+}
+
+int ConPutChar(Uint32 c)
+{
+ TCHAR sbuf[1];
+#ifdef HARDDEBUG
+ fprintf(stderr,"ConPutChar: %d\n",(int) c);
+ fflush(stderr);
+#endif
+ sbuf[0] = c;
+ write_outbuf(sbuf, 1);
+ return 1;
+}
+
+static int GetXFromLine(HDC hdc, int hscroll, int xpos,ScreenLine_t *pLine)
+{
+ SIZE size;
+ int hscrollPix = hscroll * cxChar;
+
+ if (pLine == NULL) {
+ return 0;
+ }
+
+ if (pLine->width < xpos) {
+ return (canvasColumns-hscroll)*cxChar;
+ }
+ /* Not needed (?): SelectObject(hdc,CreateFontIndirect(&logfont)); */
+ if (GetTextExtentPoint32(hdc,pLine->text,xpos,&size)) {
+#ifdef HARDDEBUG
+ fprintf(stderr,"size.cx:%d\n",(int)size.cx);
+ fflush(stderr);
+#endif
+ if (hscrollPix >= size.cx) {
+ return 0;
+ }
+ return ((int) size.cx) - hscrollPix;
+ } else {
+ return (xpos-hscroll)*cxChar;
+ }
+}
+
+static int GetXFromCurrentY(HDC hdc, int hscroll, int xpos) {
+ return GetXFromLine(hdc, hscroll, xpos, GetLineFromY(cur_y));
+}
+
+void ConSetCursor(int from, int to)
+{ TCHAR cmd[9];
+ int *p;
+ //DebugBreak();
+ cmd[0] = SET_CURSOR;
+ /*
+ * XXX Expect trouble on CPUs which don't allow misaligned read and writes.
+ */
+ p = (int *)&cmd[1];
+ *p++ = from;
+ *p = to;
+ write_outbuf(cmd, 1 + (2*sizeof(int)/sizeof(TCHAR)));
+}
+
+void ConPrintf(char *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ (void) con_vprintf(format, va);
+ va_end(va);
+}
+
+void ConBeep(void)
+{
+ SendMessage(hClientWnd, WM_CONBEEP, 0L, 0L);
+}
+
+int ConReadInput(Uint32 *data, int num_chars)
+{
+ TCHAR *buf;
+ int nread;
+ WaitForSingleObject(console_input,INFINITE);
+ nread = num_chars = min(num_chars,inbuf.wrPos-inbuf.rdPos);
+ buf = &inbuf.data[inbuf.rdPos];
+ inbuf.rdPos += nread;
+ while (nread--)
+ *data++ = *buf++;
+ if (inbuf.rdPos >= inbuf.wrPos) {
+ inbuf.rdPos = 0;
+ inbuf.wrPos = 0;
+ ResetEvent(console_input_event);
+ }
+ ReleaseSemaphore(console_input,1,NULL);
+ return num_chars;
+}
+
+int ConGetKey(void)
+{
+ Uint32 c;
+ WaitForSingleObject(console_input,INFINITE);
+ ResetEvent(console_input_event);
+ inbuf.rdPos = inbuf.wrPos = 0;
+ ReleaseSemaphore(console_input,1,NULL);
+ WaitForSingleObject(console_input_event,INFINITE);
+ ConReadInput(&c, 1);
+ return (int) c;
+}
+
+int ConGetColumns(void)
+{
+ return (int) canvasColumns; /* 32bit atomic on windows */
+}
+
+int ConGetRows(void) {
+ return (int) canvasRows;
+}
+
+
+static HINSTANCE hInstance;
+extern HMODULE beam_module;
+
+static unsigned __stdcall
+ConThreadInit(LPVOID param)
+{
+ MSG msg;
+ WNDCLASSEX wndclass;
+ int iCmdShow;
+ STARTUPINFO StartupInfo;
+ HACCEL hAccel;
+ int x, y, w, h;
+ struct title_buf title;
+
+ /*DebugBreak();*/
+ hInstance = GetModuleHandle(NULL);
+ StartupInfo.dwFlags = 0;
+ GetStartupInfo(&StartupInfo);
+ iCmdShow = StartupInfo.dwFlags & STARTF_USESHOWWINDOW ?
+ StartupInfo.wShowWindow : SW_SHOWDEFAULT;
+
+ LoadUserPreferences();
+
+ /* frame window class */
+ wndclass.cbSize = sizeof (wndclass);
+ wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNCLIENT;
+ wndclass.lpfnWndProc = FrameWndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = hInstance;
+ wndclass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(1));
+ wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wndclass.hbrBackground = NULL;
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = szFrameClass;
+ wndclass.hIconSm = LoadIcon (hInstance, MAKEINTRESOURCE(1));
+ RegisterClassExW (&wndclass);
+
+ /* client window class */
+ wndclass.cbSize = sizeof (wndclass);
+ wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+ wndclass.lpfnWndProc = ClientWndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = hInstance;
+ wndclass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(1));
+ wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wndclass.hbrBackground = CreateSolidBrush(bkgColor);
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = szClientClass;
+ wndclass.hIconSm = LoadIcon (hInstance, MAKEINTRESOURCE(1));
+ RegisterClassExW (&wndclass);
+
+ InitCommonControls();
+ init_buffers();
+
+ nBufLines = 0;
+ buffer_top = cur_line = ConNewLine();
+ cur_line->next = buffer_bottom = ConNewLine();
+ buffer_bottom->prev = cur_line;
+
+ /* Create Frame Window */
+ window_title(&title);
+ hFrameWnd = CreateWindowEx(0, szFrameClass, title.name,
+ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
+ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
+ NULL,LoadMenu(beam_module,MAKEINTRESOURCE(1)),
+ hInstance,NULL);
+ free_window_title(&title);
+
+ /* XXX OTP-5522:
+ The window position is not saved correctly and if the window
+ is closed when minimized, it's not possible to start werl again
+ with the window open. Temporary fix so far is to ignore saved values
+ and always start with initial settings. */
+ /* Original: if (winPos.left == -1) { */
+ /* Temporary: if (1) { */
+ if (1) {
+
+ /* initial window position */
+ x = 0;
+ y = 0;
+ w = cxChar*LINE_LENGTH+FRAME_WIDTH+GetSystemMetrics(SM_CXVSCROLL);
+ h = cyChar*30+FRAME_HEIGHT;
+ } else {
+ /* saved window position */
+ x = winPos.left;
+ y = winPos.top;
+ w = winPos.right - x;
+ h = winPos.bottom - y;
+ }
+ SetWindowPos(hFrameWnd, NULL, x, y, w, h, SWP_NOZORDER);
+
+ ShowWindow(hFrameWnd, iCmdShow);
+ UpdateWindow(hFrameWnd);
+
+ hAccel = LoadAccelerators(beam_module,MAKEINTRESOURCE(1));
+
+ ReleaseSemaphore(console_input, 1, NULL);
+ ReleaseSemaphore(console_output, 1, NULL);
+
+
+ /* Main message loop */
+ while (GetMessage (&msg, NULL, 0, 0))
+ {
+ if (!TranslateAccelerator(hFrameWnd,hAccel,&msg))
+ {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+ }
+ /*
+ PostQuitMessage() results in WM_QUIT which makes GetMessage()
+ return 0 (which stops the main loop). Before we return from
+ the console thread, the ctrl_handler is called to do erl_exit.
+ */
+ (*ctrl_handler)(CTRL_CLOSE_EVENT);
+ return msg.wParam;
+}
+
+static LRESULT CALLBACK
+FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
+{
+ RECT r;
+ int cy,i,bufsize;
+ TCHAR c;
+ unsigned long l;
+ TCHAR buf[128];
+ struct title_buf title;
+
+ switch (iMsg) {
+ case WM_CREATE:
+ /* client window creation */
+ window_title(&title);
+ hClientWnd = CreateWindowEx(WS_EX_CLIENTEDGE, szClientClass, title.name,
+ WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ hwnd, (HMENU)0, hInstance, NULL);
+ free_window_title(&title);
+ hTBWnd = InitToolBar(hwnd);
+ UpdateWindow (hClientWnd);
+ return 0;
+ case WM_SIZE :
+ if (IsWindowVisible(hTBWnd)) {
+ SendMessage(hTBWnd,TB_AUTOSIZE,0,0L);
+ GetWindowRect(hTBWnd,&r);
+ cy = r.bottom-r.top;
+ } else cy = 0;
+ MoveWindow(hClientWnd,0,cy,LOWORD(lParam),HIWORD(lParam)-cy,TRUE);
+ return 0;
+ case WM_ERASEBKGND:
+ return 1;
+ case WM_SETFOCUS :
+ CreateCaret(hClientWnd, NULL, cxChar, cyChar);
+ SetCaretPos(GetXFromCurrentY(GetDC(hwnd),0,cur_x), (cur_y-iVscrollPos)*cyChar);
+ ShowCaret(hClientWnd);
+ return 0;
+ case WM_KILLFOCUS:
+ HideCaret(hClientWnd);
+ DestroyCaret();
+ return 0;
+ case WM_INITMENUPOPUP :
+ if (lParam == 0) /* File popup menu */
+ {
+ EnableMenuItem((HMENU)wParam, IDMENU_STARTLOG,
+ logfile ? MF_GRAYED : MF_ENABLED);
+ EnableMenuItem((HMENU)wParam, IDMENU_STOPLOG,
+ logfile ? MF_ENABLED : MF_GRAYED);
+ return 0;
+ }
+ else if (lParam == 1) /* Edit popup menu */
+ {
+ EnableMenuItem((HMENU)wParam, IDMENU_COPY,
+ fTextSelected ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem((HMENU)wParam, IDMENU_PASTE,
+ IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED);
+ return 0;
+ }
+ else if (lParam == 3) /* View popup menu */
+ {
+ CheckMenuItem((HMENU)wParam,IDMENU_TOOLBAR,
+ IsWindowVisible(hTBWnd) ? MF_CHECKED : MF_UNCHECKED);
+ return 0;
+ }
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR) lParam)->code) {
+ case TTN_NEEDTEXT:
+ {
+ LPTOOLTIPTEXT lpttt;
+ lpttt = (LPTOOLTIPTEXT) lParam;
+ lpttt->hinst = hInstance;
+ /* check for combobox handle */
+ if (lpttt->uFlags&TTF_IDISHWND) {
+ if ((lpttt->hdr.idFrom == (UINT) hComboWnd)) {
+ lstrcpy(lpttt->lpszText,TEXT("Command History"));
+ break;
+ }
+ }
+ /* check for toolbar buttons */
+ switch (lpttt->hdr.idFrom) {
+ case IDMENU_COPY:
+ lstrcpy(lpttt->lpszText,TEXT("Copy (Ctrl+C)"));
+ break;
+ case IDMENU_PASTE:
+ lstrcpy(lpttt->lpszText,TEXT("Paste (Ctrl+V)"));
+ break;
+ case IDMENU_FONT:
+ lstrcpy(lpttt->lpszText,TEXT("Fonts"));
+ break;
+ case IDMENU_ABOUT:
+ lstrcpy(lpttt->lpszText,TEXT("Help"));
+ break;
+ }
+ }
+ }
+ break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDMENU_STARTLOG:
+ OpenLogFile(hwnd);
+ return 0;
+ case IDMENU_STOPLOG:
+ CloseLogFile(hwnd);
+ return 0;
+ case IDMENU_EXIT:
+ SendMessage(hwnd, WM_CLOSE, 0, 0L);
+ return 0;
+ case IDMENU_COPY:
+ if (fTextSelected)
+ OnEditCopy(hClientWnd);
+ return 0;
+ case IDMENU_PASTE:
+ OnEditPaste(hClientWnd);
+ return 0;
+ case IDMENU_SELALL:
+ OnEditSelAll(hClientWnd);
+ return 0;
+ case IDMENU_FONT:
+ if (ConChooseFont(hClientWnd)) {
+ ConSetFont(hClientWnd);
+ }
+ SaveUserPreferences();
+ return 0;
+ case IDMENU_SELECTBKG:
+ ConChooseColor(hClientWnd);
+ SaveUserPreferences();
+ return 0;
+ case IDMENU_TOOLBAR:
+ if (toolbarVisible) {
+ ShowWindow(hTBWnd,SW_HIDE);
+ toolbarVisible = FALSE;
+ } else {
+ ShowWindow(hTBWnd,SW_SHOW);
+ toolbarVisible = TRUE;
+ }
+ GetClientRect(hwnd,&r);
+ PostMessage(hwnd,WM_SIZE,0,MAKELPARAM(r.right,r.bottom));
+ return 0;
+ case IDMENU_ABOUT:
+ DialogBox(beam_module,TEXT("AboutBox"),hwnd,AboutDlgProc);
+ return 0;
+ case ID_COMBOBOX:
+ switch (HIWORD(wParam)) {
+ case CBN_SELENDOK:
+ i = SendMessage(hComboWnd,CB_GETCURSEL,0,0);
+ if (i != CB_ERR) {
+ buf[0] = 0x01; /* CTRL+A */
+ buf[1] = 0x0B; /* CTRL+K */
+ bufsize = SendMessage(hComboWnd,CB_GETLBTEXT,i,(LPARAM)&buf[2]);
+ if (bufsize != CB_ERR)
+ write_inbuf(buf,bufsize+2);
+ SetFocus(hwnd);
+ }
+ break;
+ case CBN_SELENDCANCEL:
+ break;
+ }
+ break;
+ case ID_BREAK: /* CTRL+BRK */
+ /* pass on break char if the ctrl_handler is disabled */
+ if ((*ctrl_handler)(CTRL_C_EVENT) == FALSE) {
+ c = 0x03;
+ write_inbuf(&c,1);
+ }
+ return 0;
+ }
+ break;
+ case WM_KEYDOWN :
+ switch (wParam) {
+ case VK_UP: c = 'P'-'@'; break;
+ case VK_DOWN : c = 'N'-'@'; break;
+ case VK_RIGHT : c = 'F'-'@'; break;
+ case VK_LEFT : c = 'B'-'@'; break;
+ case VK_DELETE : c = 'D' -'@'; break;
+ case VK_HOME : c = 'A'-'@'; break;
+ case VK_END : c = 'E'-'@'; break;
+ case VK_RETURN : AddToCmdHistory(); return 0;
+ case VK_PRIOR : /* PageUp */
+ PostMessage(hClientWnd, WM_VSCROLL, SB_PAGEUP, 0);
+ return 0;
+ case VK_NEXT : /* PageDown */
+ PostMessage(hClientWnd, WM_VSCROLL, SB_PAGEDOWN, 0);
+ return 0;
+ default: return 0;
+ }
+ write_inbuf(&c, 1);
+ return 0;
+ case WM_CHAR:
+ c = (TCHAR)wParam;
+ write_inbuf(&c,1);
+ return 0;
+ case WM_CLOSE :
+ break;
+ case WM_DESTROY :
+ SaveUserPreferences();
+ destroyed = TRUE;
+ PostQuitMessage(0);
+ return 0;
+ case WM_SAVE_PREFS :
+ SaveUserPreferences();
+ return 0;
+ }
+ return DefWindowProc(hwnd, iMsg, wParam, lParam);
+}
+
+static BOOL
+Client_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
+{
+ ConFontInitialize(hwnd);
+ cur_x = cur_y = 0;
+ iVscrollPos = 0;
+ iHscrollPos = 0;
+ return TRUE;
+}
+
+static void
+Client_OnPaint(HWND hwnd)
+{
+ ScreenLine_t *pLine;
+ int x,y,i,iTop,iBot;
+ PAINTSTRUCT ps;
+ RECT rcInvalid;
+ HDC hdc;
+
+ hdc = BeginPaint(hwnd, &ps);
+ rcInvalid = ps.rcPaint;
+ hdc = ps.hdc;
+ iTop = max(0, iVscrollPos + rcInvalid.top/cyChar);
+ iBot = min(nBufLines, iVscrollPos + rcInvalid.bottom/cyChar+1);
+ pLine = GetLineFromY(iTop);
+ for (i = iTop; i < iBot && pLine != NULL; i++) {
+ y = cyChar*(i-iVscrollPos);
+ x = -cxChar*iHscrollPos;
+ TextOut(hdc, x, y, &pLine->text[0], pLine->width);
+ pLine = pLine->next;
+ }
+ if (fTextSelected || fSelecting) {
+ InvertSelectionArea(hwnd);
+ }
+ SetCaretPos(GetXFromCurrentY(hdc,iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
+ EndPaint(hwnd, &ps);
+}
+#ifdef HARDDEBUG
+static void dump_linebufs(void) {
+ char *buff;
+ ScreenLine_t *s = buffer_top;
+ fprintf(stderr,"LinebufDump------------------------\n");
+ while(s) {
+ if (s == buffer_top) fprintf(stderr,"BT-> ");
+ if (s == buffer_bottom) fprintf(stderr,"BB-> ");
+ if (s == cur_line) fprintf(stderr,"CL-> ");
+
+ buff = (char *) ALLOC(s->width+1);
+ memcpy(buff,s->text,s->width);
+ buff[s->width] = '\0';
+ fprintf(stderr,"{\"%s\",%d,%d}\n",buff,s->newline,s->allocated);
+ FREE(buff);
+ s = s->next;
+ }
+ fprintf(stderr,"LinebufDumpEnd---------------------\n");
+ fflush(stderr);
+}
+#endif
+
+static void reorganize_linebufs(HWND hwnd) {
+ ScreenLine_t *otop = buffer_top;
+ ScreenLine_t *obot = buffer_bottom;
+ ScreenLine_t *next;
+ int i,cpos;
+
+ cpos = 0;
+ i = nBufLines - cur_y;
+ while (i > 1) {
+ cpos += obot->width;
+ obot = obot->prev;
+ i--;
+ }
+ cpos += (obot->width - cur_x);
+#ifdef HARDDEBUG
+ fprintf(stderr,"nBufLines = %d, cur_x = %d, cur_y = %d, cpos = %d\n",
+ nBufLines,cur_x,cur_y,cpos);
+ fflush(stderr);
+#endif
+
+
+ nBufLines = 0;
+ buffer_top = cur_line = ConNewLine();
+ cur_line->next = buffer_bottom = ConNewLine();
+ buffer_bottom->prev = cur_line;
+
+ cur_x = cur_y = 0;
+ iVscrollPos = 0;
+ iHscrollPos = 0;
+
+ while(otop) {
+ for(i=0;i<otop->width;++i) {
+ cur_line->text[cur_x] = otop->text[i];
+ cur_x++;
+ if (cur_x > cur_line->width)
+ cur_line->width = cur_x;
+ if (GetXFromCurrentY(GetDC(hwnd),0,cur_x) + cxChar >
+ (LINE_LENGTH * cxChar)) {
+ ConCarriageFeed(0);
+ }
+ }
+ if (otop->newline) {
+ ConCarriageFeed(1);
+ /*ConScrollScreen();*/
+ }
+ next = otop->next;
+ FREE(otop->text);
+ FREE(otop);
+ otop = next;
+ }
+ while (cpos) {
+ cur_x--;
+ if (cur_x < 0) {
+ cur_y--;
+ cur_line = cur_line->prev;
+ cur_x = cur_line->width-1;
+ }
+ cpos--;
+ }
+ SetCaretPos(GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
+#ifdef HARDDEBUG
+ fprintf(stderr,"canvasColumns = %d,nBufLines = %d, cur_x = %d, cur_y = %d\n",
+ canvasColumns,nBufLines,cur_x,cur_y);
+ fflush(stderr);
+#endif
+}
+
+
+static void
+Client_OnSize(HWND hwnd, UINT state, int cx, int cy)
+{
+ RECT r;
+ SCROLLBARINFO sbi;
+ int w,h,columns;
+ int scrollheight;
+ cxClient = cx;
+ cyClient = cy;
+ set_scroll_info(hwnd);
+ GetClientRect(hwnd,&r);
+ w = r.right - r.left;
+ h = r.bottom - r.top;
+ sbi.cbSize = sizeof(SCROLLBARINFO);
+ if (!GetScrollBarInfo(hwnd, OBJID_HSCROLL,&sbi) ||
+ (sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE)) {
+ scrollheight = 0;
+ } else {
+ scrollheight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
+ }
+ canvasRows = (h - scrollheight) / cyChar;
+ if (canvasRows < DEF_CANVAS_ROWS) {
+ canvasRows = DEF_CANVAS_ROWS;
+ }
+ columns = (w - GetSystemMetrics(SM_CXVSCROLL)) /cxChar;
+ if (columns < DEF_CANVAS_COLUMNS)
+ columns = DEF_CANVAS_COLUMNS;
+ if (columns != canvasColumns) {
+ canvasColumns = columns;
+ /*dump_linebufs();*/
+ reorganize_linebufs(hwnd);
+ fSelecting = fTextSelected = FALSE;
+ InvalidateRect(hwnd, NULL, TRUE);
+#ifdef HARDDEBUG
+ fprintf(stderr,"Paint: cols = %d, rows = %d\n",canvasColumns,canvasRows);
+ fflush(stderr);
+#endif
+ }
+
+ SetCaretPos(GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
+}
+
+static void calc_charpoint_from_point(HDC dc, int x, int y, int y_offset, POINT *pt)
+{
+ int r;
+ int hscrollPix = iHscrollPos * cxChar;
+
+ pt->y = y/cyChar + iVscrollPos + y_offset;
+
+ if (x > (LINE_LENGTH-iHscrollPos) * cxChar) {
+ x = (LINE_LENGTH-iHscrollPos) * cxChar;
+ }
+ if (pt->y - y_offset > 0 && GetLineFromY(pt->y - y_offset) == NULL) {
+ pt->y = nBufLines - 1 + y_offset;
+ pt->x = GetLineFromY(pt->y - y_offset)->width;
+ } else {
+ for (pt->x = 1;
+ (r = GetXFromLine(dc, 0, pt->x, GetLineFromY(pt->y - y_offset))) != 0 &&
+ (r - hscrollPix) < x;
+ ++(pt->x))
+ ;
+ if ((r - hscrollPix) > x)
+ --(pt->x);
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"pt->x = %d, iHscrollPos = %d\n",(int) pt->x, iHscrollPos);
+ fflush(stderr);
+#endif
+ if (pt->x <= 0) {
+ pt->x = x/cxChar + iHscrollPos;
+ }
+ }
+}
+
+
+static void
+Client_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
+{
+ int r;
+ SetFocus(GetParent(hwnd)); /* In case combobox steals the focus */
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"OnLButtonDown fSelecting = %d, fTextSelected = %d:\n",
+ fSelecting,fTextSelected);
+ fflush(stderr);
+#endif
+ if (fTextSelected) {
+ InvertSelectionArea(hwnd);
+ }
+ fTextSelected = FALSE;
+
+ calc_charpoint_from_point(GetDC(hwnd), x, y, 0, &editBeg);
+
+ editEnd.x = editBeg.x;
+ editEnd.y = editBeg.y + 1;
+ fSelecting = TRUE;
+ SetCapture(hwnd);
+}
+
+static void
+Client_OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
+{
+ if (fTextSelected) {
+ fSelecting = TRUE;
+ Client_OnMouseMove(hwnd,x,y,keyFlags);
+ fSelecting = FALSE;
+ }
+}
+
+static void
+Client_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
+{
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"OnLButtonUp fSelecting = %d, fTextSelected = %d:\n",
+ fSelecting,fTextSelected);
+ fprintf(stderr,"(Beg.x = %d, Beg.y = %d, "
+ "End.x = %d, End.y = %d)\n",editBeg.x,editBeg.y,
+ editEnd.x,editEnd.y);
+#endif
+ if (fSelecting &&
+ !(editBeg.x == editEnd.x && editBeg.y == (editEnd.y - 1))) {
+ fTextSelected = TRUE;
+ }
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"OnLButtonUp fTextSelected = %d:\n",
+ fTextSelected);
+ fflush(stderr);
+#endif
+ fSelecting = FALSE;
+ ReleaseCapture();
+}
+
+#define EMPTY_RECT(R) \
+(((R).bottom - (R).top == 0) || ((R).right - (R).left == 0))
+#define ABS(X) (((X)< 0) ? -1 * (X) : X)
+#define DIFF(A,B) ABS(((int)(A)) - ((int)(B)))
+
+static int diff_sel_area(RECT old[3], RECT new[3], RECT result[6])
+{
+ int absposold = old[0].left + old[0].top * canvasColumns;
+ int absposnew = new[0].left + new[0].top * canvasColumns;
+ int absendold = absposold, absendnew = absposnew;
+ int i, x, ret = 0;
+ int abspos[2],absend[2];
+ for(i = 0; i < 3; ++i) {
+ if (!EMPTY_RECT(old[i])) {
+ absendold += (old[i].right - old[i].left) *
+ (old[i].bottom - old[i].top);
+ }
+ if (!EMPTY_RECT(new[i])) {
+ absendnew += (new[i].right - new[i].left) *
+ (new[i].bottom - new[i].top);
+ }
+ }
+ abspos[0] = min(absposold, absposnew);
+ absend[0] = DIFF(absposold, absposnew) + abspos[0];
+ abspos[1] = min(absendold, absendnew);
+ absend[1] = DIFF(absendold, absendnew) + abspos[1];
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"abspos[0] = %d, absend[0] = %d, abspos[1] = %d, absend[1] = %d\n",abspos[0],absend[0],abspos[1],absend[1]);
+ fflush(stderr);
+#endif
+ i = 0;
+ for (x = 0; x < 2; ++x) {
+ if (abspos[x] != absend[x]) {
+ int consumed = 0;
+ result[i].left = abspos[x] % canvasColumns;
+ result[i].top = abspos[x] / canvasColumns;
+ result[i].bottom = result[i].top + 1;
+ if ((absend[x] - abspos[x]) + result[i].left < canvasColumns) {
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"Nowrap, %d < canvasColumns\n",
+ (absend[x] - abspos[x]) + result[i].left);
+ fflush(stderr);
+#endif
+ result[i].right = (absend[x] - abspos[x]) + result[i].left;
+ consumed += result[i].right - result[i].left;
+ } else {
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"Wrap, %d >= canvasColumns\n",
+ (absend[x] - abspos[x]) + result[i].left);
+ fflush(stderr);
+#endif
+ result[i].right = canvasColumns;
+ consumed += result[i].right - result[i].left;
+ if (absend[x] - abspos[x] - consumed >= canvasColumns) {
+ ++i;
+ result[i].top = result[i-1].bottom;
+ result[i].left = 0;
+ result[i].right = canvasColumns;
+ result[i].bottom = (absend[x] - abspos[x] - consumed) / canvasColumns + result[i].top;
+ consumed += (result[i].bottom - result[i].top) * canvasColumns;
+ }
+ if (absend[x] - abspos[x] - consumed > 0) {
+ ++i;
+ result[i].top = result[i-1].bottom;
+ result[i].bottom = result[i].top + 1;
+ result[i].left = 0;
+ result[i].right = absend[x] - abspos[x] - consumed;
+ }
+ }
+ ++i;
+ }
+ }
+#ifdef HARD_SEL_DEBUG
+ if (i > 2) {
+ int x;
+ fprintf(stderr,"i = %d\n",i);
+ fflush(stderr);
+ for (x = 0; x < i; ++x) {
+ fprintf(stderr, "result[%d]: top = %d, left = %d, "
+ "bottom = %d. right = %d\n",
+ x, result[x].top, result[x].left,
+ result[x].bottom, result[x].right);
+ }
+ }
+#endif
+ return i;
+}
+
+
+
+static void calc_sel_area(RECT rects[3], POINT beg, POINT end)
+{
+ /* These are not really rects and points, these are character
+ based positions, need to be multiplied by cxChar and cyChar to
+ make up canvas coordinates */
+ memset(rects,0,3*sizeof(RECT));
+ rects[0].left = beg.x;
+ rects[0].top = beg.y;
+ rects[0].bottom = beg.y+1;
+ if (end.y - beg.y == 1) { /* Only one row */
+ rects[0].right = end.x;
+ goto out;
+ }
+ rects[0].right = canvasColumns;
+ if (end.y - beg.y > 2) {
+ rects[1].left = 0;
+ rects[1].top = rects[0].bottom;
+ rects[1].right = canvasColumns;
+ rects[1].bottom = end.y - 1;
+ }
+ rects[2].left = 0;
+ rects[2].top = end.y - 1;
+ rects[2].bottom = end.y;
+ rects[2].right = end.x;
+
+ out:
+#ifdef HARD_SEL_DEBUG
+ {
+ int i;
+ fprintf(stderr,"beg.x = %d, beg.y = %d, end.x = %d, end.y = %d\n",
+ beg.x,beg.y,end.x,end.y);
+ for (i = 0; i < 3; ++i) {
+ fprintf(stderr,"[%d] left = %d, top = %d, "
+ "right = %d, bottom = %d\n",
+ i, rects[i].left, rects[i].top,
+ rects[i].right, rects[i].bottom);
+ }
+ fflush(stderr);
+ }
+#endif
+ return;
+}
+
+static void calc_sel_area_turned(RECT rects[3], POINT eBeg, POINT eEnd) {
+ POINT from,to;
+ if (eBeg.y >= eEnd.y ||
+ (eBeg.y == eEnd.y - 1 && eBeg.x > eEnd.x)) {
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"Reverting (Beg.x = %d, Beg.y = %d, "
+ "End.x = %d, End.y = %d)\n",eBeg.x,eBeg.y,
+ eEnd.x,eEnd.y);
+ fflush(stderr);
+#endif
+ from.x = eEnd.x;
+ from.y = eEnd.y - 1;
+ to.x = eBeg.x;
+ to.y = eBeg.y + 1;
+ calc_sel_area(rects,from,to);
+ } else {
+ calc_sel_area(rects,eBeg,eEnd);
+ }
+}
+
+
+static void InvertSelectionArea(HWND hwnd)
+{
+ RECT rects[3];
+ POINT from,to;
+ int i;
+ calc_sel_area_turned(rects,editBeg,editEnd);
+ for (i = 0; i < 3; ++i) {
+ if (!EMPTY_RECT(rects[i])) {
+ from.x = rects[i].left;
+ to.x = rects[i].right;
+ from.y = rects[i].top;
+ to.y = rects[i].bottom;
+ DrawSelection(hwnd,from,to);
+ }
+ }
+}
+
+static void
+Client_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
+{
+ if (fSelecting) {
+ RECT rold[3], rnew[3], rupdate[6];
+ int num_updates,i,r;
+ POINT from,to;
+ calc_sel_area_turned(rold,editBeg,editEnd);
+
+ calc_charpoint_from_point(GetDC(hwnd), x, y, 1, &editEnd);
+
+ calc_sel_area_turned(rnew,editBeg,editEnd);
+ num_updates = diff_sel_area(rold,rnew,rupdate);
+ for (i = 0; i < num_updates;++i) {
+ from.x = rupdate[i].left;
+ to.x = rupdate[i].right;
+ from.y = rupdate[i].top;
+ to.y = rupdate[i].bottom;
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"from: x=%d,y=%d, to: x=%d, y=%d\n",
+ from.x, from.y,to.x,to.y);
+ fflush(stderr);
+#endif
+ DrawSelection(hwnd,from,to);
+ }
+ }
+}
+
+static void
+Client_OnVScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)
+{
+ int iVscroll;
+
+ switch(code) {
+ case SB_LINEDOWN:
+ iVscroll = 1;
+ break;
+ case SB_LINEUP:
+ iVscroll = -1;
+ break;
+ case SB_PAGEDOWN:
+ iVscroll = max(1, cyClient/cyChar);
+ break;
+ case SB_PAGEUP:
+ iVscroll = min(-1, -cyClient/cyChar);
+ break;
+ case SB_THUMBTRACK:
+ iVscroll = pos - iVscrollPos;
+ break;
+ default:
+ iVscroll = 0;
+ }
+ iVscroll = max(-iVscrollPos, min(iVscroll, iVscrollMax-iVscrollPos));
+ if (iVscroll != 0) {
+ iVscrollPos += iVscroll;
+ ScrollWindowEx(hwnd, 0, -cyChar*iVscroll, NULL, NULL,
+ NULL, NULL, SW_ERASE | SW_INVALIDATE);
+ SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);
+ iVscroll = GetScrollPos(hwnd, SB_VERT);
+ UpdateWindow(hwnd);
+ }
+}
+
+static void
+Client_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)
+{
+ int iHscroll, curCharWidth = cxClient/cxChar;
+
+ switch(code) {
+ case SB_LINEDOWN:
+ iHscroll = 1;
+ break;
+ case SB_LINEUP:
+ iHscroll = -1;
+ break;
+ case SB_PAGEDOWN:
+ iHscroll = max(1,curCharWidth-1);
+ break;
+ case SB_PAGEUP:
+ iHscroll = min(-1,-(curCharWidth-1));
+ break;
+ case SB_THUMBTRACK:
+ iHscroll = pos - iHscrollPos;
+ break;
+ default:
+ iHscroll = 0;
+ }
+ iHscroll = max(-iHscrollPos, min(iHscroll, iHscrollMax-iHscrollPos-(curCharWidth-1)));
+ if (iHscroll != 0) {
+ iHscrollPos += iHscroll;
+ ScrollWindow(hwnd, -cxChar*iHscroll, 0, NULL, NULL);
+ SetScrollPos(hwnd, SB_HORZ, iHscrollPos, TRUE);
+ UpdateWindow(hwnd);
+ }
+}
+
+static LRESULT CALLBACK
+ClientWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (iMsg) {
+ HANDLE_MSG(hwnd, WM_CREATE, Client_OnCreate);
+ HANDLE_MSG(hwnd, WM_SIZE, Client_OnSize);
+ HANDLE_MSG(hwnd, WM_PAINT, Client_OnPaint);
+ HANDLE_MSG(hwnd, WM_LBUTTONDOWN, Client_OnLButtonDown);
+ HANDLE_MSG(hwnd, WM_RBUTTONDOWN, Client_OnRButtonDown);
+ HANDLE_MSG(hwnd, WM_LBUTTONUP, Client_OnLButtonUp);
+ HANDLE_MSG(hwnd, WM_MOUSEMOVE, Client_OnMouseMove);
+ HANDLE_MSG(hwnd, WM_VSCROLL, Client_OnVScroll);
+ HANDLE_MSG(hwnd, WM_HSCROLL, Client_OnHScroll);
+ case WM_CONBEEP:
+ if (0) Beep(440, 400);
+ return 0;
+ case WM_CONTEXT:
+ ConDrawText(hwnd);
+ return 0;
+ case WM_CLOSE:
+ break;
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ return 0;
+ }
+ return DefWindowProc (hwnd, iMsg, wParam, lParam);
+}
+
+static void
+LoadUserPreferences(void)
+{
+ DWORD size;
+ DWORD res;
+ DWORD type;
+
+ /* default prefs */
+ GetObject(GetStockObject(SYSTEM_FIXED_FONT),sizeof(LOGFONT),(PSTR)&logfont);
+ fgColor = GetSysColor(COLOR_WINDOWTEXT);
+ bkgColor = GetSysColor(COLOR_WINDOW);
+ winPos.left = -1;
+ toolbarVisible = TRUE;
+
+ if (RegCreateKeyEx(HKEY_CURRENT_USER, USER_KEY, 0, 0,
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
+ &key, &res) != ERROR_SUCCESS)
+ return;
+ has_key = TRUE;
+ if (res == REG_CREATED_NEW_KEY)
+ return;
+ size = sizeof(logfont);
+ res = RegQueryValueEx(key,TEXT("Font"),NULL,&type,(LPBYTE)&logfont,&size);
+ size = sizeof(fgColor);
+ res = RegQueryValueEx(key,TEXT("FgColor"),NULL,&type,(LPBYTE)&fgColor,&size);
+ size = sizeof(bkgColor);
+ res = RegQueryValueEx(key,TEXT("BkColor"),NULL,&type,(LPBYTE)&bkgColor,&size);
+ size = sizeof(winPos);
+ res = RegQueryValueEx(key,TEXT("Pos"),NULL,&type,(LPBYTE)&winPos,&size);
+ size = sizeof(toolbarVisible);
+ res = RegQueryValueEx(key,TEXT("Toolbar"),NULL,&type,(LPBYTE)&toolbarVisible,&size);
+}
+
+static void
+SaveUserPreferences(void)
+{
+ WINDOWPLACEMENT wndPlace;
+
+ if (has_key == TRUE) {
+ RegSetValueEx(key,TEXT("Font"),0,REG_BINARY,(CONST BYTE *)&logfont,sizeof(LOGFONT));
+ RegSetValueEx(key,TEXT("FgColor"),0,REG_DWORD,(CONST BYTE *)&fgColor,sizeof(fgColor));
+ RegSetValueEx(key,TEXT("BkColor"),0,REG_DWORD,(CONST BYTE *)&bkgColor,sizeof(bkgColor));
+ RegSetValueEx(key,TEXT("Toolbar"),0,REG_DWORD,(CONST BYTE *)&toolbarVisible,sizeof(toolbarVisible));
+
+ wndPlace.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(hFrameWnd,&wndPlace);
+ /* If wndPlace.showCmd == SW_MINIMIZE, then the window is minimized.
+ We don't care, wndPlace.rcNormalPosition always holds the last known position. */
+ winPos = wndPlace.rcNormalPosition;
+ RegSetValueEx(key,TEXT("Pos"),0,REG_BINARY,(CONST BYTE *)&winPos,sizeof(winPos));
+ }
+}
+
+
+static void
+set_scroll_info(HWND hwnd)
+{
+ SCROLLINFO info;
+ int hScrollBy;
+ /*
+ * Set vertical scrolling range and scroll box position.
+ */
+
+ iVscrollMax = nBufLines-1;
+ iVscrollPos = min(iVscrollPos, iVscrollMax);
+ info.cbSize = sizeof(info);
+ info.fMask = SIF_PAGE|SIF_RANGE|SIF_POS;
+ info.nMin = 0;
+ info.nPos = iVscrollPos;
+ info.nPage = min(cyClient/cyChar, iVscrollMax);
+ info.nMax = iVscrollMax;
+ SetScrollInfo(hwnd, SB_VERT, &info, TRUE);
+
+ /*
+ * Set horizontal scrolling range and scroll box position.
+ */
+
+ iHscrollMax = LINE_LENGTH-1;
+ hScrollBy = max(0, (iHscrollPos - (iHscrollMax-cxClient/cxChar))*cxChar);
+ iHscrollPos = min(iHscrollPos, iHscrollMax);
+ info.nPos = iHscrollPos;
+ info.nPage = cxClient/cxChar;
+ info.nMax = iHscrollMax;
+ SetScrollInfo(hwnd, SB_HORZ, &info, TRUE);
+ /*ScrollWindow(hwnd, hScrollBy, 0, NULL, NULL);*/
+}
+
+
+static void
+ensure_line_below(void)
+{
+ if (cur_line->next == NULL) {
+ if (nBufLines >= lines_to_save) {
+ ScreenLine_t* pLine = buffer_top->next;
+ FREE(buffer_top->text);
+ FREE(buffer_top);
+ buffer_top = pLine;
+ buffer_top->prev = NULL;
+ nBufLines--;
+ }
+ cur_line->next = ConNewLine();
+ cur_line->next->prev = cur_line;
+ buffer_bottom = cur_line->next;
+ set_scroll_info(hClientWnd);
+ }
+}
+
+static ScreenLine_t*
+ConNewLine(void)
+{
+ ScreenLine_t *pLine;
+
+ pLine = (ScreenLine_t *)ALLOC(sizeof(ScreenLine_t));
+ if (!pLine)
+ return NULL;
+ pLine->text = (TCHAR *) ALLOC(canvasColumns * sizeof(TCHAR));
+#ifdef HARDDEBUG
+ pLine->allocated = canvasColumns;
+#endif
+ pLine->width = 0;
+ pLine->prev = pLine->next = NULL;
+ pLine->newline = 0;
+ nBufLines++;
+ return pLine;
+}
+
+static ScreenLine_t*
+GetLineFromY(int y)
+{
+ ScreenLine_t *pLine = buffer_top;
+ int i;
+
+ for (i = 0; i < nBufLines && pLine != NULL; i++) {
+ if (i == y)
+ return pLine;
+ pLine = pLine->next;
+ }
+ return NULL;
+}
+
+void ConCarriageFeed(int hard_newline)
+{
+ cur_x = 0;
+ ensure_line_below();
+ cur_line->newline = hard_newline;
+ cur_line = cur_line->next;
+ if (cur_y < nBufLines-1) {
+ cur_y++;
+ } else if (iVscrollPos > 0) {
+ iVscrollPos--;
+ }
+}
+
+/*
+ * Scroll screen if cursor is not visible.
+ */
+static void
+ConScrollScreen(void)
+{
+ if (cur_y >= iVscrollPos + cyClient/cyChar) {
+ int iVscroll;
+
+ iVscroll = cur_y - iVscrollPos - cyClient/cyChar + 1;
+ iVscrollPos += iVscroll;
+ ScrollWindowEx(hClientWnd, 0, -cyChar*iVscroll, NULL, NULL,
+ NULL, NULL, SW_ERASE | SW_INVALIDATE);
+ SetScrollPos(hClientWnd, SB_VERT, iVscrollPos, TRUE);
+ UpdateWindow(hClientWnd);
+ }
+}
+
+static void
+DrawSelection(HWND hwnd, POINT pt1, POINT pt2)
+{
+ HDC hdc;
+ int width,height;
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"pt1.x = %d, pt1.y = %d, pt2.x = %d, pt2.y = %d\n",
+ (int) pt1.x, (int) pt1.y, (int) pt2.x, (int) pt2.y);
+#endif
+ pt1.x = GetXFromLine(GetDC(hwnd),iHscrollPos,pt1.x,GetLineFromY(pt1.y));
+ pt2.x = GetXFromLine(GetDC(hwnd),iHscrollPos,pt2.x,GetLineFromY(pt2.y-1));
+ pt1.y -= iVscrollPos;
+ pt2.y -= iVscrollPos;
+ pt1.y *= cyChar;
+ pt2.y *= cyChar;
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"pt1.x = %d, pt1.y = %d, pt2.x = %d, pt2.y = %d\n",
+ (int) pt1.x, (int) pt1.y, (int) pt2.x, (int) pt2.y);
+ fflush(stderr);
+#endif
+ width = pt2.x-pt1.x;
+ height = pt2.y - pt1.y;
+ hdc = GetDC(hwnd);
+ PatBlt(hdc,pt1.x,pt1.y,width,height,DSTINVERT);
+ ReleaseDC(hwnd,hdc);
+}
+
+static void
+OnEditCopy(HWND hwnd)
+{
+ HGLOBAL hMem;
+ TCHAR *pMem;
+ ScreenLine_t *pLine;
+ RECT rects[3];
+ POINT from,to;
+ int i,j,sum,len;
+ if (editBeg.y >= editEnd.y ||
+ (editBeg.y == editEnd.y - 1 && editBeg.x > editEnd.x)) {
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"CopyReverting (Beg.x = %d, Beg.y = %d, "
+ "End.x = %d, End.y = %d)\n",editBeg.x,editBeg.y,
+ editEnd.x,editEnd.y);
+ fflush(stderr);
+#endif
+ from.x = editEnd.x;
+ from.y = editEnd.y - 1;
+ to.x = editBeg.x;
+ to.y = editBeg.y + 1;
+ calc_sel_area(rects,from,to);
+ } else {
+ calc_sel_area(rects,editBeg,editEnd);
+ }
+ sum = 1;
+ for (i = 0; i < 3; ++i) {
+ if (!EMPTY_RECT(rects[i])) {
+ pLine = GetLineFromY(rects[i].top);
+ for (j = rects[i].top; j < rects[i].bottom ;++j) {
+ if (pLine == NULL) {
+ sum += 2;
+ break;
+ }
+ if (pLine->width > rects[i].left) {
+ sum += (pLine->width < rects[i].right) ?
+ pLine->width - rects[i].left :
+ rects[i].right - rects[i].left;
+ }
+ if(pLine->newline && rects[i].right >= pLine->width) {
+ sum += 2;
+ }
+ pLine = pLine->next;
+ }
+ }
+ }
+#ifdef HARD_SEL_DEBUG
+ fprintf(stderr,"sum = %d\n",sum);
+ fflush(stderr);
+#endif
+ hMem = GlobalAlloc(GHND, sum * sizeof(TCHAR));
+ pMem = GlobalLock(hMem);
+ for (i = 0; i < 3; ++i) {
+ if (!EMPTY_RECT(rects[i])) {
+ pLine = GetLineFromY(rects[i].top);
+ for (j = rects[i].top; j < rects[i].bottom; ++j) {
+ if (pLine == NULL) {
+ memcpy(pMem,TEXT("\r\n"),2 * sizeof(TCHAR));
+ pMem += 2;
+ break;
+ }
+ if (pLine->width > rects[i].left) {
+ len = (pLine->width < rects[i].right) ?
+ pLine->width - rects[i].left :
+ rects[i].right - rects[i].left;
+ memcpy(pMem,pLine->text + rects[i].left,len * sizeof(TCHAR));
+ pMem +=len;
+ }
+ if(pLine->newline && rects[i].right >= pLine->width) {
+ memcpy(pMem,TEXT("\r\n"),2 * sizeof(TCHAR));
+ pMem += 2;
+ }
+ pLine = pLine->next;
+ }
+ }
+ }
+ *pMem = TEXT('\0');
+ /* Flash de selection area to give user feedback about copying */
+ InvertSelectionArea(hwnd);
+ Sleep(100);
+ InvertSelectionArea(hwnd);
+
+ OpenClipboard(hwnd);
+ EmptyClipboard();
+ GlobalUnlock(hMem);
+ SetClipboardData(CF_UNICODETEXT,hMem);
+ CloseClipboard();
+}
+
+/* XXX:PaN Tchar or char? */
+static void
+OnEditPaste(HWND hwnd)
+{
+ HANDLE hClipMem;
+ TCHAR *pClipMem,*pMem,*pMem2;
+ if (!OpenClipboard(hwnd))
+ return;
+ if ((hClipMem = GetClipboardData(CF_UNICODETEXT)) != NULL) {
+ pClipMem = GlobalLock(hClipMem);
+ pMem = (TCHAR *)ALLOC(GlobalSize(hClipMem) * sizeof(TCHAR));
+ pMem2 = pMem;
+ while ((*pMem2 = *pClipMem) != TEXT('\0')) {
+ if (*pClipMem == TEXT('\r'))
+ *pMem2 = TEXT('\n');
+ ++pMem2;
+ ++pClipMem;
+ }
+ GlobalUnlock(hClipMem);
+ write_inbuf(pMem, _tcsclen(pMem));
+ }
+ CloseClipboard();
+}
+
+static void
+OnEditSelAll(HWND hwnd)
+{
+ editBeg.x = 0;
+ editBeg.y = 0;
+ editEnd.x = LINE_LENGTH-1;
+ editEnd.y = cur_y;
+ fTextSelected = TRUE;
+ InvalidateRect(hwnd, NULL, TRUE);
+}
+
+UINT APIENTRY CFHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)
+{
+ /* Hook procedure for font dialog box */
+ HWND hOwner;
+ RECT rc,rcOwner,rcDlg;
+ switch (iMsg) {
+ case WM_INITDIALOG:
+ /* center dialogbox within its owner window */
+ if ((hOwner = GetParent(hDlg)) == NULL)
+ hOwner = GetDesktopWindow();
+ GetWindowRect(hOwner, &rcOwner);
+ GetWindowRect(hDlg, &rcDlg);
+ CopyRect(&rc, &rcOwner);
+ OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
+ OffsetRect(&rc, -rc.left, -rc.top);
+ OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
+ SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2),
+ rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE);
+ return 1;
+ default:
+ break;
+ }
+ return 0; /* Let the default procedure process the message */
+}
+
+static BOOL
+ConChooseFont(HWND hwnd)
+{
+ HDC hdc;
+ hdc = GetDC(hwnd);
+ cf.lStructSize = sizeof(CHOOSEFONT);
+ cf.hwndOwner = hwnd;
+ cf.hDC = NULL;
+ cf.lpLogFont = &logfont;
+ cf.iPointSize = 0;
+ cf.Flags = CF_INITTOLOGFONTSTRUCT|CF_SCREENFONTS|CF_FIXEDPITCHONLY|CF_EFFECTS|CF_ENABLEHOOK;
+ cf.rgbColors = GetTextColor(hdc);
+ cf.lCustData = 0L;
+ cf.lpfnHook = CFHookProc;
+ cf.lpTemplateName = NULL;
+ cf.hInstance = NULL;
+ cf.lpszStyle = NULL;
+ cf.nFontType = 0;
+ cf.nSizeMin = 0;
+ cf.nSizeMax = 0;
+ ReleaseDC(hwnd,hdc);
+ return ChooseFont(&cf);
+}
+
+static void
+ConFontInitialize(HWND hwnd)
+{
+ HDC hdc;
+ TEXTMETRIC tm;
+ HFONT hFont;
+
+ hFont = CreateFontIndirect(&logfont);
+ hdc = GetDC(hwnd);
+ SelectObject(hdc, hFont);
+ SetTextColor(hdc,fgColor);
+ SetBkColor(hdc,bkgColor);
+ GetTextMetrics(hdc, &tm);
+ cxChar = tm.tmAveCharWidth;
+ cxCharMax = tm.tmMaxCharWidth;
+ cyChar = tm.tmHeight + tm.tmExternalLeading;
+ ReleaseDC(hwnd, hdc);
+}
+
+static void
+ConSetFont(HWND hwnd)
+{
+ HDC hdc;
+ TEXTMETRIC tm;
+ HFONT hFontNew;
+
+ hFontNew = CreateFontIndirect(&logfont);
+ SendMessage(hComboWnd,WM_SETFONT,(WPARAM)hFontNew,
+ MAKELPARAM(1,0));
+ hdc = GetDC(hwnd);
+ DeleteObject(SelectObject(hdc, hFontNew));
+ GetTextMetrics(hdc, &tm);
+ cxChar = tm.tmAveCharWidth;
+ cxCharMax = tm.tmMaxCharWidth;
+ cyChar = tm.tmHeight + tm.tmExternalLeading;
+ fgColor = cf.rgbColors;
+ SetTextColor(hdc,fgColor);
+ ReleaseDC(hwnd, hdc);
+ set_scroll_info(hwnd);
+ HideCaret(hwnd);
+ if (DestroyCaret()) {
+ CreateCaret(hwnd, NULL, cxChar, cyChar);
+ SetCaretPos(GetXFromCurrentY(hdc,iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
+ }
+ ShowCaret(hwnd);
+ InvalidateRect(hwnd, NULL, TRUE);
+}
+
+UINT APIENTRY
+CCHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)
+{
+ /* Hook procedure for choose color dialog box */
+ HWND hOwner;
+ RECT rc,rcOwner,rcDlg;
+ switch (iMsg) {
+ case WM_INITDIALOG:
+ /* center dialogbox within its owner window */
+ if ((hOwner = GetParent(hDlg)) == NULL)
+ hOwner = GetDesktopWindow();
+ GetWindowRect(hOwner, &rcOwner);
+ GetWindowRect(hDlg, &rcDlg);
+ CopyRect(&rc, &rcOwner);
+ OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
+ OffsetRect(&rc, -rc.left, -rc.top);
+ OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
+ SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2),
+ rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE);
+ return 1;
+ default:
+ break;
+ }
+ return 0; /* Let the default procedure process the message */
+}
+
+void ConChooseColor(HWND hwnd)
+{
+ CHOOSECOLOR cc;
+ static COLORREF acrCustClr[16];
+ HBRUSH hbrush;
+ HDC hdc;
+
+ /* Initialize CHOOSECOLOR */
+ ZeroMemory(&cc, sizeof(CHOOSECOLOR));
+ cc.lStructSize = sizeof(CHOOSECOLOR);
+ cc.hwndOwner = hwnd;
+ cc.lpCustColors = (LPDWORD) acrCustClr;
+ cc.rgbResult = bkgColor;
+ cc.lpfnHook = CCHookProc;
+ cc.Flags = CC_FULLOPEN|CC_RGBINIT|CC_SOLIDCOLOR|CC_ENABLEHOOK;
+
+ if (ChooseColor(&cc)==TRUE) {
+ bkgColor = cc.rgbResult;
+ hdc = GetDC(hwnd);
+ SetBkColor(hdc,bkgColor);
+ ReleaseDC(hwnd,hdc);
+ hbrush = CreateSolidBrush(bkgColor);
+ DeleteObject((HBRUSH)SetClassLong(hClientWnd,GCL_HBRBACKGROUND,(LONG)hbrush));
+ InvalidateRect(hwnd,NULL,TRUE);
+ }
+}
+
+UINT APIENTRY OFNHookProc(HWND hwndDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)
+{
+ /* Hook procedure for open file dialog box */
+ HWND hOwner,hDlg;
+ RECT rc,rcOwner,rcDlg;
+ hDlg = GetParent(hwndDlg);
+ switch (iMsg) {
+ case WM_INITDIALOG:
+ /* center dialogbox within its owner window */
+ if ((hOwner = GetParent(hDlg)) == NULL)
+ hOwner = GetDesktopWindow();
+ GetWindowRect(hOwner, &rcOwner);
+ GetWindowRect(hDlg, &rcDlg);
+ CopyRect(&rc, &rcOwner);
+ OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
+ OffsetRect(&rc, -rc.left, -rc.top);
+ OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
+ SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2),
+ rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE);
+ return 1;
+ default:
+ break;
+ }
+ return 0; /* the let default procedure process the message */
+}
+
+static void
+GetFileName(HWND hwnd, TCHAR *pFile)
+{
+ /* Open the File Open dialog box and */
+ /* retrieve the file name */
+ OPENFILENAME ofn;
+ TCHAR szFilterSpec [128] = TEXT("logfiles (*.log)\0*.log\0All files (*.*)\0*.*\0\0");
+ #define MAXFILENAME 256
+ TCHAR szFileName[MAXFILENAME];
+ TCHAR szFileTitle[MAXFILENAME];
+
+ /* these need to be filled in */
+ _tcscpy(szFileName, TEXT("erlshell.log"));
+ _tcscpy(szFileTitle, TEXT("")); /* must be NULL */
+
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = NULL;
+ ofn.lpstrFilter = szFilterSpec;
+ ofn.lpstrCustomFilter = NULL;
+ ofn.nMaxCustFilter = 0;
+ ofn.nFilterIndex = 0;
+ ofn.lpstrFile = szFileName;
+ ofn.nMaxFile = MAXFILENAME;
+ ofn.lpstrInitialDir = NULL;
+ ofn.lpstrFileTitle = szFileTitle;
+ ofn.nMaxFileTitle = MAXFILENAME;
+ ofn.lpstrTitle = TEXT("Open logfile");
+ ofn.lpstrDefExt = TEXT("log");
+ ofn.Flags = OFN_CREATEPROMPT|OFN_HIDEREADONLY|OFN_EXPLORER|OFN_ENABLEHOOK|OFN_NOCHANGEDIR; /* OFN_NOCHANGEDIR only works in Vista :( */
+ ofn.lpfnHook = OFNHookProc;
+
+ if (!GetOpenFileName ((LPOPENFILENAME)&ofn)){
+ *pFile = TEXT('\0');
+ } else {
+ _tcscpy(pFile, ofn.lpstrFile);
+ }
+}
+
+void OpenLogFile(HWND hwnd)
+{
+ /* open a file for logging */
+ TCHAR filename[_MAX_PATH];
+
+ GetFileName(hwnd, filename);
+ if (filename[0] == '\0')
+ return;
+ if (NULL == (logfile = _tfopen(filename,TEXT("w,ccs=UNICODE"))))
+ return;
+}
+
+void CloseLogFile(HWND hwnd)
+{
+ /* close log file */
+ fclose(logfile);
+ logfile = NULL;
+}
+
+void LogFileWrite(TCHAR *buf, int num_chars)
+{
+ /* write to logfile */
+ int from,to;
+ while (num_chars-- > 0) {
+ switch (*buf) {
+ case SET_CURSOR:
+ buf++;
+ from = *((int *)buf);
+ buf += sizeof(int)/sizeof(TCHAR);
+ to = *((int *)buf);
+ buf += (sizeof(int)/sizeof(TCHAR))-1;
+ num_chars -= 2 * (sizeof(int)/sizeof(TCHAR));
+ // Wont seek in Unicode file, sorry...
+ // fseek(logfile,to-from *sizeof(TCHAR),SEEK_CUR);
+ break;
+ default:
+ _fputtc(*buf,logfile);
+ break;
+ }
+ buf++;
+ }
+}
+
+static void
+init_buffers(void)
+{
+ inbuf.data = (TCHAR *) ALLOC(BUFSIZE * sizeof(TCHAR));
+ outbuf.data = (TCHAR *) ALLOC(BUFSIZE * sizeof(TCHAR));
+ inbuf.size = BUFSIZE;
+ inbuf.rdPos = inbuf.wrPos = 0;
+ outbuf.size = BUFSIZE;
+ outbuf.rdPos = outbuf.wrPos = 0;
+}
+
+static int
+check_realloc(buffer_t *buf, int num_chars)
+{
+ if (buf->wrPos + num_chars >= buf->size) {
+ if (buf->size > MAXBUFSIZE)
+ return 0;
+ buf->size += num_chars + BUFSIZE;
+ if (!(buf->data = (TCHAR *)REALLOC(buf->data, buf->size * sizeof(TCHAR)))) {
+ buf->size = buf->rdPos = buf->wrPos = 0;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+write_inbuf(TCHAR *data, int num_chars)
+{
+ TCHAR *buf;
+ int nwrite;
+ WaitForSingleObject(console_input,INFINITE);
+ if (!check_realloc(&inbuf,num_chars)) {
+ ReleaseSemaphore(console_input,1,NULL);
+ return -1;
+ }
+ buf = &inbuf.data[inbuf.wrPos];
+ inbuf.wrPos += num_chars;
+ nwrite = num_chars;
+ while (nwrite--)
+ *buf++ = *data++;
+ SetEvent(console_input_event);
+ ReleaseSemaphore(console_input,1,NULL);
+ return num_chars;
+}
+
+static int
+write_outbuf(TCHAR *data, int num_chars)
+{
+ TCHAR *buf;
+ int nwrite;
+
+ WaitForSingleObject(console_output,INFINITE);
+ if (!check_realloc(&outbuf, num_chars)) {
+ ReleaseSemaphore(console_output,1,NULL);
+ return -1;
+ }
+ if (outbuf.rdPos == outbuf.wrPos)
+ PostMessage(hClientWnd, WM_CONTEXT, 0L, 0L);
+ buf = &outbuf.data[outbuf.wrPos];
+ outbuf.wrPos += num_chars;
+ nwrite = num_chars;
+ while (nwrite--)
+ *buf++ = *data++;
+ ReleaseSemaphore(console_output,1,NULL);
+ return num_chars;
+}
+
+BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hOwner;
+ RECT rc,rcOwner,rcDlg;
+
+ switch (iMsg) {
+ case WM_INITDIALOG:
+ /* center dialogbox within its owner window */
+ if ((hOwner = GetParent(hDlg)) == NULL)
+ hOwner = GetDesktopWindow();
+ GetWindowRect(hOwner, &rcOwner);
+ GetWindowRect(hDlg, &rcDlg);
+ CopyRect(&rc, &rcOwner);
+ OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
+ OffsetRect(&rc, -rc.left, -rc.top);
+ OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
+ SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2),
+ rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE);
+ SetDlgItemText(hDlg, ID_VERSIONSTRING,
+ TEXT("Erlang emulator version ") TEXT(ERLANG_VERSION));
+ return TRUE;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(hDlg,0);
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static void
+ConDrawText(HWND hwnd)
+{
+ int num_chars;
+ int nchars;
+ TCHAR *buf;
+ int from, to;
+ int dl;
+ int dc;
+ RECT rc;
+
+ WaitForSingleObject(console_output, INFINITE);
+ nchars = 0;
+ num_chars = outbuf.wrPos - outbuf.rdPos;
+ buf = &outbuf.data[outbuf.rdPos];
+ if (logfile != NULL)
+ LogFileWrite(buf, num_chars);
+
+
+#ifdef HARDDEBUG
+ {
+ TCHAR *bu = (TCHAR *) ALLOC((num_chars+1) * sizeof(TCHAR));
+ memcpy(bu,buf,num_chars * sizeof(TCHAR));
+ bu[num_chars]='\0';
+ fprintf(stderr,TEXT("ConDrawText\"%s\"\n"),bu);
+ FREE(bu);
+ fflush(stderr);
+ }
+#endif
+ /*
+ * Don't draw any text in the window; just update the line buffers
+ * and invalidate the appropriate part of the window. The window
+ * will be updated on the next WM_PAINT message.
+ */
+
+ while (num_chars-- > 0) {
+ switch (*buf) {
+ case '\r':
+ break;
+ case '\n':
+ if (nchars > 0) {
+ rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars);
+ rc.right = rc.left + cxCharMax*nchars;
+ rc.top = cyChar * (cur_y-iVscrollPos);
+ rc.bottom = rc.top + cyChar;
+ InvalidateRect(hwnd, &rc, TRUE);
+ nchars = 0;
+ }
+ ConCarriageFeed(1);
+ ConScrollScreen();
+ break;
+ case SET_CURSOR:
+ if (nchars > 0) {
+ rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars);
+ rc.right = rc.left + cxCharMax*nchars;
+ rc.top = cyChar * (cur_y-iVscrollPos);
+ rc.bottom = rc.top + cyChar;
+ InvalidateRect(hwnd, &rc, TRUE);
+ nchars = 0;
+ }
+ buf++;
+ from = *((int *)buf);
+ buf += sizeof(int)/sizeof(TCHAR);
+ to = *((int *)buf);
+ buf += (sizeof(int)/sizeof(TCHAR))-1;
+ num_chars -= 2 * (sizeof(int)/sizeof(TCHAR));
+ while (to > from) {
+ cur_x++;
+ if (GetXFromCurrentY(GetDC(hwnd),0,cur_x)+cxChar >
+ (LINE_LENGTH * cxChar)) {
+ cur_x = 0;
+ cur_y++;
+ ensure_line_below();
+ cur_line = cur_line->next;
+ }
+ from++;
+ }
+ while (to < from) {
+ cur_x--;
+ if (cur_x < 0) {
+ cur_y--;
+ cur_line = cur_line->prev;
+ cur_x = cur_line->width-1;
+ }
+ from--;
+ }
+
+ break;
+ default:
+ nchars++;
+ cur_line->text[cur_x] = *buf;
+ cur_x++;
+ if (cur_x > cur_line->width)
+ cur_line->width = cur_x;
+ if (GetXFromCurrentY(GetDC(hwnd),0,cur_x)+cxChar >
+ (LINE_LENGTH * cxChar)) {
+ if (nchars > 0) {
+ rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars);
+ rc.right = rc.left + cxCharMax*nchars;
+ rc.top = cyChar * (cur_y-iVscrollPos);
+ rc.bottom = rc.top + cyChar;
+ InvalidateRect(hwnd, &rc, TRUE);
+ }
+ ConCarriageFeed(0);
+ nchars = 0;
+ }
+ }
+ buf++;
+ }
+ if (nchars > 0) {
+ rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars);
+ rc.right = rc.left + cxCharMax*nchars;
+ rc.top = cyChar * (cur_y-iVscrollPos);
+ rc.bottom = rc.top + cyChar;
+ InvalidateRect(hwnd, &rc, TRUE);
+ }
+ ConScrollScreen();
+ SetCaretPos(GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
+ outbuf.wrPos = outbuf.rdPos = 0;
+ ReleaseSemaphore(console_output, 1, NULL);
+}
+
+static void
+AddToCmdHistory(void)
+{
+ int i;
+ int size;
+ Uint32 *buf;
+ wchar_t cmdBuf[128];
+
+ if (llen != 0) {
+ for (i = 0, size = 0; i < llen-1; i++) {
+ /*
+ * Find end of prompt.
+ */
+ if ((lbuf[i] == '>') && lbuf[i+1] == ' ') {
+ buf = &lbuf[i+2];
+ size = llen-i-2;
+ break;
+ }
+ }
+ if (size > 0 && size < 128) {
+ for (i = 0;i < size; ++i) {
+ cmdBuf[i] = (wchar_t) buf[i];
+ }
+ cmdBuf[size] = 0;
+ SendMessage(hComboWnd,CB_INSERTSTRING,0,(LPARAM)cmdBuf);
+ }
+ }
+}
+
+static TBBUTTON tbb[] =
+{
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+ 0, IDMENU_COPY, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0,
+ 1, IDMENU_PASTE, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0,
+ 2, IDMENU_FONT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0,
+ 3, IDMENU_ABOUT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0,
+ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0,
+};
+
+static TBADDBITMAP tbbitmap =
+{
+ HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
+};
+
+
+static HWND
+InitToolBar(HWND hwndParent)
+{
+ int x,y,cx;
+ HWND hwndTB,hwndTT;
+ RECT r;
+ TOOLINFO ti;
+ HFONT hFontNew;
+ DWORD backgroundColor = GetSysColor(COLOR_BTNFACE);
+ COLORMAP colorMap;
+ colorMap.from = RGB(192, 192, 192);
+ colorMap.to = backgroundColor;
+
+ /* Create toolbar window with tooltips */
+ hwndTB = CreateWindowEx(0,TOOLBARCLASSNAME,(TCHAR *)NULL,
+ WS_CHILD|CCS_TOP|WS_CLIPSIBLINGS|TBSTYLE_TOOLTIPS,
+ 0,0,0,0,hwndParent,
+ (HMENU)2,hInstance,NULL);
+ SendMessage(hwndTB,TB_BUTTONSTRUCTSIZE,
+ (WPARAM) sizeof(TBBUTTON),0);
+ tbbitmap.hInst = NULL;
+ tbbitmap.nID = (UINT) CreateMappedBitmap(beam_module, 1,0, &colorMap, 1);
+ SendMessage(hwndTB, TB_ADDBITMAP, (WPARAM) 4,
+ (WPARAM) &tbbitmap);
+ SendMessage(hwndTB,TB_ADDBUTTONS, (WPARAM) 30,
+ (LPARAM) (LPTBBUTTON) tbb);
+ if (toolbarVisible)
+ ShowWindow(hwndTB, SW_SHOW);
+
+ /* Create combobox window */
+ SendMessage(hwndTB,TB_GETITEMRECT,0,(LPARAM)&r);
+ x = r.left; y = r.top;
+ SendMessage(hwndTB,TB_GETITEMRECT,23,(LPARAM)&r);
+ cx = r.right - x + 1;
+ hComboWnd = CreateWindow(TEXT("combobox"),NULL,WS_VSCROLL|WS_CHILD|WS_VISIBLE|CBS_DROPDOWNLIST,
+ x,y,cx,100,hwndParent,(HMENU)ID_COMBOBOX, hInstance,NULL);
+ SetParent(hComboWnd,hwndTB);
+ hFontNew = CreateFontIndirect(&logfont);
+ SendMessage(hComboWnd,WM_SETFONT,(WPARAM)hFontNew,
+ MAKELPARAM(1,0));
+
+ /* Add tooltip for combo box */
+ ZeroMemory(&ti,sizeof(TOOLINFO));
+ ti.cbSize = sizeof(TOOLINFO);
+ ti.uFlags = TTF_IDISHWND|TTF_CENTERTIP|TTF_SUBCLASS;
+ ti.hwnd = hwndTB;;
+ ti.uId = (UINT)hComboWnd;
+ ti.lpszText = LPSTR_TEXTCALLBACK;
+ hwndTT = (HWND)SendMessage(hwndTB,TB_GETTOOLTIPS,0,0);
+ SendMessage(hwndTT,TTM_ADDTOOL,0,(LPARAM)&ti);
+
+ return hwndTB;
+}
+
+static void
+window_title(struct title_buf *tbuf)
+{
+ int res, i;
+ size_t bufsz = TITLE_BUF_SZ;
+ unsigned char charbuff[TITLE_BUF_SZ];
+
+ res = erl_drv_getenv("ERL_WINDOW_TITLE", charbuff, &bufsz);
+ if (res < 0)
+ tbuf->name = erlang_window_title;
+ else if (res == 0) {
+ for (i = 0; i < bufsz; ++i) {
+ tbuf->buf[i] = charbuff[i];
+ }
+ tbuf->buf[bufsz - 1] = 0;
+ tbuf->name = &tbuf->buf[0];
+ } else {
+ char *buf = ALLOC(bufsz);
+ if (!buf)
+ tbuf->name = erlang_window_title;
+ else {
+ while (1) {
+ char *newbuf;
+ res = erl_drv_getenv("ERL_WINDOW_TITLE", buf, &bufsz);
+ if (res <= 0) {
+ if (res == 0) {
+ TCHAR *wbuf = ALLOC(bufsz *sizeof(TCHAR));
+ for (i = 0; i < bufsz ; ++i) {
+ wbuf[i] = buf[i];
+ }
+ wbuf[bufsz - 1] = 0;
+ FREE(buf);
+ tbuf->name = wbuf;
+ } else {
+ tbuf->name = erlang_window_title;
+ FREE(buf);
+ }
+ break;
+ }
+ newbuf = REALLOC(buf, bufsz);
+ if (newbuf)
+ buf = newbuf;
+ else {
+ tbuf->name = erlang_window_title;
+ FREE(buf);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+free_window_title(struct title_buf *tbuf)
+{
+ if (tbuf->name != erlang_window_title && tbuf->name != &tbuf->buf[0])
+ FREE(tbuf->name);
+}
diff --git a/erts/emulator/drivers/win32/win_con.h b/erts/emulator/drivers/win32/win_con.h
new file mode 100644
index 0000000000..d46af86ca5
--- /dev/null
+++ b/erts/emulator/drivers/win32/win_con.h
@@ -0,0 +1,39 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2007-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * External API for the windows console (aka werl window)
+ * used by ttsl_drv.c
+ */
+#ifndef _WIN_CON_H_VISITED
+#define _WIN_CON_H_VISITED 1
+void ConNormalExit(void);
+void ConWaitForExit(void);
+void ConSetCtrlHandler(BOOL (WINAPI *handler)(DWORD));
+int ConPutChar(Uint32 c);
+void ConSetCursor(int from, int to);
+void ConPrintf(char *format, ...);
+void ConVprintf(char *format, va_list va);
+void ConBeep(void);
+int ConReadInput(Uint32 *data, int nbytes);
+int ConGetKey(void);
+int ConGetColumns(void);
+int ConGetRows(void);
+void ConInit(void);
+#endif /* _WIN_CON_H_VISITED */
diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c
new file mode 100644
index 0000000000..89aaad31da
--- /dev/null
+++ b/erts/emulator/drivers/win32/win_efile.c
@@ -0,0 +1,1426 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Purpose: Provides file and directory operations for Windows.
+ */
+
+#include <windows.h>
+#include "sys.h"
+#include <ctype.h>
+
+#include "erl_efile.h"
+
+/*
+ * Microsoft-specific function to map a WIN32 error code to a Posix errno.
+ */
+
+#define ISSLASH(a) ((a) == '\\' || (a) == '/')
+
+#define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR)
+#define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG)
+
+#define IS_DOT_OR_DOTDOT(s) \
+ (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0')))
+
+#ifndef INVALID_FILE_ATTRIBUTES
+#define INVALID_FILE_ATTRIBUTES ((DWORD) 0xFFFFFFFF)
+#endif
+
+static int check_error(int result, Efile_error* errInfo);
+static int set_error(Efile_error* errInfo);
+static int IsRootUNCName(const char* path);
+static int extract_root(char* name);
+static unsigned short dos_to_posix_mode(int attr, const char *name);
+
+static int errno_map(DWORD last_error) {
+
+ switch (last_error) {
+ case ERROR_SUCCESS:
+ return 0;
+ case ERROR_INVALID_FUNCTION:
+ case ERROR_INVALID_DATA:
+ case ERROR_INVALID_PARAMETER:
+ case ERROR_INVALID_TARGET_HANDLE:
+ case ERROR_INVALID_CATEGORY:
+ case ERROR_NEGATIVE_SEEK:
+ return EINVAL;
+ case ERROR_DIR_NOT_EMPTY:
+ return EEXIST;
+ case ERROR_BAD_FORMAT:
+ return ENOEXEC;
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_NO_MORE_FILES:
+ return ENOENT;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ return EMFILE;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_INVALID_ACCESS:
+ case ERROR_CURRENT_DIRECTORY:
+ case ERROR_SHARING_VIOLATION:
+ case ERROR_LOCK_VIOLATION:
+ case ERROR_INVALID_PASSWORD:
+ case ERROR_DRIVE_LOCKED:
+ return EACCES;
+ case ERROR_INVALID_HANDLE:
+ return EBADF;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case ERROR_OUT_OF_STRUCTURES:
+ return ENOMEM;
+ case ERROR_INVALID_DRIVE:
+ case ERROR_BAD_UNIT:
+ case ERROR_NOT_READY:
+ case ERROR_REM_NOT_LIST:
+ case ERROR_DUP_NAME:
+ case ERROR_BAD_NETPATH:
+ case ERROR_NETWORK_BUSY:
+ case ERROR_DEV_NOT_EXIST:
+ case ERROR_BAD_NET_NAME:
+ return ENXIO;
+ case ERROR_NOT_SAME_DEVICE:
+ return EXDEV;
+ case ERROR_WRITE_PROTECT:
+ return EROFS;
+ case ERROR_BAD_LENGTH:
+ case ERROR_BUFFER_OVERFLOW:
+ return E2BIG;
+ case ERROR_SEEK:
+ case ERROR_SECTOR_NOT_FOUND:
+ return ESPIPE;
+ case ERROR_NOT_DOS_DISK:
+ return ENODEV;
+ case ERROR_GEN_FAILURE:
+ return ENODEV;
+ case ERROR_SHARING_BUFFER_EXCEEDED:
+ case ERROR_NO_MORE_SEARCH_HANDLES:
+ return EMFILE;
+ case ERROR_HANDLE_EOF:
+ case ERROR_BROKEN_PIPE:
+ return EPIPE;
+ case ERROR_HANDLE_DISK_FULL:
+ case ERROR_DISK_FULL:
+ return ENOSPC;
+ case ERROR_NOT_SUPPORTED:
+ return ENOTSUP;
+ case ERROR_FILE_EXISTS:
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_CANNOT_MAKE:
+ return EEXIST;
+ case ERROR_ALREADY_ASSIGNED:
+ return EBUSY;
+ case ERROR_NO_PROC_SLOTS:
+ return EAGAIN;
+ case ERROR_ARENA_TRASHED:
+ case ERROR_INVALID_BLOCK:
+ case ERROR_BAD_ENVIRONMENT:
+ case ERROR_BAD_COMMAND:
+ case ERROR_CRC:
+ case ERROR_OUT_OF_PAPER:
+ case ERROR_READ_FAULT:
+ case ERROR_WRITE_FAULT:
+ case ERROR_WRONG_DISK:
+ case ERROR_NET_WRITE_FAULT:
+ return EIO;
+ default: /* not to do with files I expect. */
+ return EIO;
+ }
+}
+
+static int
+check_error(int result, Efile_error* errInfo)
+{
+ if (result < 0) {
+ errInfo->posix_errno = errno;
+ errInfo->os_errno = GetLastError();
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Fills the provided error information structure with information
+ * with the error code given by GetLastError() and its corresponding
+ * Posix error number.
+ *
+ * Returns 0.
+ */
+
+static int
+set_error(Efile_error* errInfo)
+{
+ errInfo->posix_errno = errno_map(errInfo->os_errno = GetLastError());
+ return 0;
+}
+
+/*
+ * A writev with Unix semantics, but with Windows arguments
+ */
+static int
+win_writev(Efile_error* errInfo,
+ HANDLE fd, /* handle to file */
+ FILE_SEGMENT_ELEMENT iov[], /* array of buffer pointers */
+ DWORD *size) /* number of bytes to write */
+{
+ OVERLAPPED ov;
+ ov.Offset = 0L;
+ ov.OffsetHigh = 0L;
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (ov.hEvent == NULL)
+ return set_error(errInfo);
+ if (! write_file_gather(fd, iov, *size, NULL, &ov))
+ return set_error(errInfo);
+ if (WaitForSingleObject(ov.hEvent, INFINITE) != WAIT_OBJECT_0)
+ return set_error(errInfo);
+ if (! GetOverlappedResult(fd, &ov, size, FALSE))
+ return set_error(errInfo);
+ return 1;
+}
+
+
+
+int
+efile_mkdir(errInfo, name)
+Efile_error* errInfo; /* Where to return error codes. */
+char* name; /* Name of directory to create. */
+{
+ return check_error(mkdir(name), errInfo);
+}
+
+int
+efile_rmdir(errInfo, name)
+Efile_error* errInfo; /* Where to return error codes. */
+char* name; /* Name of directory to delete. */
+{
+ OSVERSIONINFO os;
+ DWORD attr;
+
+ if (RemoveDirectory(name) != FALSE) {
+ return 1;
+ }
+ errno = errno_map(GetLastError());
+ if (errno == EACCES) {
+ attr = GetFileAttributes(name);
+ if (attr != (DWORD) -1) {
+ if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ /*
+ * Windows 95 reports calling RemoveDirectory on a file as an
+ * EACCES, not an ENOTDIR.
+ */
+
+ errno = ENOTDIR;
+ goto end;
+ }
+
+ /*
+ * Windows 95 reports removing a non-empty directory as
+ * an EACCES, not an EEXIST. If the directory is not empty,
+ * change errno so caller knows what's going on.
+ */
+
+ os.dwOSVersionInfoSize = sizeof(os);
+ GetVersionEx(&os);
+ if (os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
+ HANDLE handle;
+ WIN32_FIND_DATA data;
+ char buffer[2*MAX_PATH];
+ int len;
+
+ len = strlen(name);
+ strcpy(buffer, name);
+ if (buffer[0] && buffer[len-1] != '\\' && buffer[len-1] != '/') {
+ strcat(buffer, "\\");
+ }
+ strcat(buffer, "*.*");
+ handle = FindFirstFile(buffer, &data);
+ if (handle != INVALID_HANDLE_VALUE) {
+ while (1) {
+ if ((strcmp(data.cFileName, ".") != 0)
+ && (strcmp(data.cFileName, "..") != 0)) {
+ /*
+ * Found something in this directory.
+ */
+
+ errno = EEXIST;
+ break;
+ }
+ if (FindNextFile(handle, &data) == FALSE) {
+ break;
+ }
+ }
+ FindClose(handle);
+ }
+ }
+ }
+ }
+
+ if (errno == ENOTEMPTY) {
+ /*
+ * Posix allows both EEXIST or ENOTEMPTY, but we'll always
+ * return EEXIST to allow easy matching in Erlang code.
+ */
+
+ errno = EEXIST;
+ }
+
+ end:
+ return check_error(-1, errInfo);
+}
+
+int
+efile_delete_file(errInfo, name)
+Efile_error* errInfo; /* Where to return error codes. */
+char* name; /* Name of file to delete. */
+{
+ DWORD attr;
+
+ if (DeleteFile(name) != FALSE) {
+ return 1;
+ }
+
+ errno = errno_map(GetLastError());
+ if (errno == EACCES) {
+ attr = GetFileAttributes(name);
+ if (attr != (DWORD) -1) {
+ if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+ /*
+ * Windows NT reports removing a directory as EACCES instead
+ * of EPERM.
+ */
+
+ errno = EPERM;
+ }
+ }
+ } else if (errno == ENOENT) {
+ attr = GetFileAttributes(name);
+ if (attr != (DWORD) -1) {
+ if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+ /*
+ * Windows 95 reports removing a directory as ENOENT instead
+ * of EPERM.
+ */
+
+ errno = EPERM;
+ }
+ }
+ } else if (errno == EINVAL) {
+ /*
+ * Windows NT reports removing a char device as EINVAL instead of
+ * EACCES.
+ */
+
+ errno = EACCES;
+ }
+
+ return check_error(-1, errInfo);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Changes the name of an existing file or directory, from src to dst.
+ * If src and dst refer to the same file or directory, does nothing
+ * and returns success. Otherwise if dst already exists, it will be
+ * deleted and replaced by src subject to the following conditions:
+ * If src is a directory, dst may be an empty directory.
+ * If src is a file, dst may be a file.
+ * In any other situation where dst already exists, the rename will
+ * fail.
+ *
+ * Some possible error codes:
+ *
+ * EACCES: src or dst parent directory can't be read and/or written.
+ * EEXIST: dst is a non-empty directory.
+ * EINVAL: src is a root directory or dst is a subdirectory of src.
+ * EISDIR: dst is a directory, but src is not.
+ * ENOENT: src doesn't exist, or src or dst is "".
+ * ENOTDIR: src is a directory, but dst is not.
+ * EXDEV: src and dst are on different filesystems.
+ *
+ * Side effects:
+ * The implementation of rename may allow cross-filesystem renames,
+ * but the caller should be prepared to emulate it with copy and
+ * delete if errno is EXDEV.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+efile_rename(errInfo, src, dst)
+Efile_error* errInfo; /* Where to return error codes. */
+char* src; /* Original name. */
+char* dst; /* New name. */
+{
+ DWORD srcAttr, dstAttr;
+
+ if (MoveFile(src, dst) != FALSE) {
+ return 1;
+ }
+
+ errno = errno_map(GetLastError());
+ srcAttr = GetFileAttributes(src);
+ dstAttr = GetFileAttributes(dst);
+ if (srcAttr == (DWORD) -1) {
+ srcAttr = 0;
+ }
+ if (dstAttr == (DWORD) -1) {
+ dstAttr = 0;
+ }
+
+ if (errno == EBADF) {
+ errno = EACCES;
+ return check_error(-1, errInfo);
+ }
+ if (errno == EACCES) {
+ decode:
+ if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) {
+ char srcPath[MAX_PATH], dstPath[MAX_PATH];
+ char *srcRest, *dstRest;
+ int size;
+
+ size = GetFullPathName(src, sizeof(srcPath), srcPath, &srcRest);
+ if ((size == 0) || (size > sizeof(srcPath))) {
+ return check_error(-1, errInfo);
+ }
+ size = GetFullPathName(dst, sizeof(dstPath), dstPath, &dstRest);
+ if ((size == 0) || (size > sizeof(dstPath))) {
+ return check_error(-1, errInfo);
+ }
+ if (srcRest == NULL) {
+ srcRest = srcPath + strlen(srcPath);
+ }
+ if (strnicmp(srcPath, dstPath, srcRest - srcPath) == 0) {
+ /*
+ * Trying to move a directory into itself.
+ */
+
+ errno = EINVAL;
+ }
+ if (extract_root(srcPath)) {
+ /*
+ * Attempt to move a root directory. Never allowed.
+ */
+ errno = EINVAL;
+ }
+
+ (void) extract_root(dstPath);
+ if (dstPath[0] == '\0') {
+ /*
+ * The filename was invalid. (Don't know why,
+ * but play it safe.)
+ */
+ errno = EINVAL;
+ }
+ if (stricmp(srcPath, dstPath) != 0) {
+ /*
+ * If src is a directory and dst filesystem != src
+ * filesystem, errno should be EXDEV. It is very
+ * important to get this behavior, so that the caller
+ * can respond to a cross filesystem rename by
+ * simulating it with copy and delete. The MoveFile
+ * system call already handles the case of moving a
+ * *file* between filesystems.
+ */
+
+ errno = EXDEV;
+ }
+ }
+
+ /*
+ * Other types of access failure is that dst is a read-only
+ * filesystem, that an open file referred to src or dest, or that
+ * src or dest specified the current working directory on the
+ * current filesystem. EACCES is returned for those cases.
+ */
+
+ } else if (errno == EEXIST) {
+ /*
+ * Reports EEXIST any time the target already exists. If it makes
+ * sense, remove the old file and try renaming again.
+ */
+
+ if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) {
+ if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) {
+ /*
+ * Overwrite empty dst directory with src directory. The
+ * following call will remove an empty directory. If it
+ * fails, it's because it wasn't empty.
+ */
+
+ if (RemoveDirectory(dst)) {
+ /*
+ * Now that that empty directory is gone, we can try
+ * renaming again. If that fails, we'll put this empty
+ * directory back, for completeness.
+ */
+
+ if (MoveFile(src, dst) != FALSE) {
+ return 1;
+ }
+
+ /*
+ * Some new error has occurred. Don't know what it
+ * could be, but report this one.
+ */
+
+ errno = errno_map(GetLastError());
+ CreateDirectory(dst, NULL);
+ SetFileAttributes(dst, dstAttr);
+ if (errno == EACCES) {
+ /*
+ * Decode the EACCES to a more meaningful error.
+ */
+
+ goto decode;
+ }
+ }
+ } else { /* (dstAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */
+ errno = ENOTDIR;
+ }
+ } else { /* (srcAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */
+ if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) {
+ errno = EISDIR;
+ } else {
+ /*
+ * Overwrite existing file by:
+ *
+ * 1. Rename existing file to temp name.
+ * 2. Rename old file to new name.
+ * 3. If success, delete temp file. If failure,
+ * put temp file back to old name.
+ */
+
+ char tempName[MAX_PATH];
+ int result, size;
+ char *rest;
+
+ size = GetFullPathName(dst, sizeof(tempName), tempName, &rest);
+ if ((size == 0) || (size > sizeof(tempName)) || (rest == NULL)) {
+ return check_error(-1, errInfo);
+ }
+ *rest = '\0';
+ result = -1;
+ if (GetTempFileName(tempName, "erlr", 0, tempName) != 0) {
+ /*
+ * Strictly speaking, need the following DeleteFile and
+ * MoveFile to be joined as an atomic operation so no
+ * other app comes along in the meantime and creates the
+ * same temp file.
+ */
+
+ DeleteFile(tempName);
+ if (MoveFile(dst, tempName) != FALSE) {
+ if (MoveFile(src, dst) != FALSE) {
+ SetFileAttributes(tempName, FILE_ATTRIBUTE_NORMAL);
+ DeleteFile(tempName);
+ return 1;
+ } else {
+ DeleteFile(dst);
+ MoveFile(tempName, dst);
+ }
+ }
+
+ /*
+ * Can't backup dst file or move src file. Return that
+ * error. Could happen if an open file refers to dst.
+ */
+
+ errno = errno_map(GetLastError());
+ if (errno == EACCES) {
+ /*
+ * Decode the EACCES to a more meaningful error.
+ */
+
+ goto decode;
+ }
+ }
+ return result;
+ }
+ }
+ }
+ return check_error(-1, errInfo);
+}
+
+int
+efile_chdir(errInfo, name)
+Efile_error* errInfo; /* Where to return error codes. */
+char* name; /* Name of directory to make current. */
+{
+ int success = check_error(chdir(name), errInfo);
+ if (!success && errInfo->posix_errno == EINVAL)
+ /* POSIXification of errno */
+ errInfo->posix_errno = ENOENT;
+ return success;
+}
+
+int
+efile_getdcwd(errInfo, drive, buffer, size)
+Efile_error* errInfo; /* Where to return error codes. */
+int drive; /* 0 - current, 1 - A, 2 - B etc. */
+char* buffer; /* Where to return the current directory. */
+size_t size; /* Size of buffer. */
+{
+ if (_getdcwd(drive, buffer, size) == NULL)
+ return check_error(-1, errInfo);
+ for ( ; *buffer; buffer++)
+ if (*buffer == '\\')
+ *buffer = '/';
+ return 1;
+}
+
+int
+efile_readdir(errInfo, name, dir_handle, buffer, size)
+Efile_error* errInfo; /* Where to return error codes. */
+char* name; /* Name of directory to open. */
+EFILE_DIR_HANDLE* dir_handle; /* Directory handle of open directory. */
+char* buffer; /* Pointer to buffer for one filename. */
+size_t size; /* Size of buffer. */
+{
+ HANDLE dir; /* Handle to directory. */
+ char wildcard[MAX_PATH]; /* Wildcard to search for. */
+ WIN32_FIND_DATA findData; /* Data found by FindFirstFile() or FindNext(). */
+
+ /*
+ * First time we must setup everything.
+ */
+
+ if (*dir_handle == NULL) {
+ int length = strlen(name);
+ char* s;
+
+ if (length+3 >= MAX_PATH) {
+ errno = ENAMETOOLONG;
+ return check_error(-1, errInfo);
+ }
+
+ strcpy(wildcard, name);
+ s = wildcard+length-1;
+ if (*s != '/' && *s != '\\')
+ *++s = '\\';
+ *++s = '*';
+ *++s = '\0';
+ DEBUGF(("Reading %s\n", wildcard));
+ dir = FindFirstFile(wildcard, &findData);
+ if (dir == INVALID_HANDLE_VALUE)
+ return set_error(errInfo);
+ *dir_handle = (EFILE_DIR_HANDLE) dir;
+
+ if (!IS_DOT_OR_DOTDOT(findData.cFileName)) {
+ strcpy(buffer, findData.cFileName);
+ return 1;
+ }
+ }
+
+
+ /*
+ * Retrieve the name of the next file using the directory handle.
+ */
+
+ dir = (HANDLE) *dir_handle;
+
+ for (;;) {
+ if (FindNextFile(dir, &findData)) {
+ if (IS_DOT_OR_DOTDOT(findData.cFileName))
+ continue;
+ strcpy(buffer, findData.cFileName);
+ return 1;
+ }
+
+ if (GetLastError() == ERROR_NO_MORE_FILES) {
+ FindClose(dir);
+ errInfo->posix_errno = errInfo->os_errno = 0;
+ return 0;
+ }
+
+ set_error(errInfo);
+ FindClose(dir);
+ return 0;
+ }
+}
+
+int
+efile_openfile(errInfo, name, flags, pfd, pSize)
+Efile_error* errInfo; /* Where to return error codes. */
+char* name; /* Name of directory to open. */
+int flags; /* Flags to use for opening. */
+int* pfd; /* Where to store the file descriptor. */
+Sint64* pSize; /* Where to store the size of the file. */
+{
+ BY_HANDLE_FILE_INFORMATION fileInfo; /* File information from a handle. */
+ HANDLE fd; /* Handle to open file. */
+ DWORD access; /* Access mode: GENERIC_READ, GENERIC_WRITE. */
+ DWORD crFlags;
+
+ switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) {
+ case EFILE_MODE_READ:
+ access = GENERIC_READ;
+ crFlags = OPEN_EXISTING;
+ break;
+ case EFILE_MODE_WRITE:
+ access = GENERIC_WRITE;
+ crFlags = CREATE_ALWAYS;
+ break;
+ case EFILE_MODE_READ_WRITE:
+ access = GENERIC_READ|GENERIC_WRITE;
+ crFlags = OPEN_ALWAYS;
+ break;
+ default:
+ errno = EINVAL;
+ check_error(-1, errInfo);
+ return 0;
+ }
+
+ if (flags & EFILE_MODE_APPEND) {
+ crFlags = OPEN_ALWAYS;
+ }
+ fd = CreateFile(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, crFlags, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ /*
+ * Check for errors.
+ */
+
+ if (fd == INVALID_HANDLE_VALUE) {
+ DWORD attr;
+
+ set_error(errInfo);
+
+ /*
+ * If the error is EACESS, the reason could be that we tried to
+ * open a directory. In that case, we'll change the error code
+ * to EISDIR.
+ */
+ if (errInfo->posix_errno &&
+ (attr = GetFileAttributes(name)) != INVALID_FILE_ATTRIBUTES &&
+ (attr & FILE_ATTRIBUTE_DIRECTORY)) {
+ errInfo->posix_errno = EISDIR;
+ }
+ return 0;
+ }
+
+ /*
+ * Get and return the length of the open file.
+ */
+
+ if (!GetFileInformationByHandle(fd, &fileInfo))
+ return set_error(errInfo);
+ *pfd = (int) fd;
+ if (pSize) {
+ *pSize = (Sint64)
+ (((Uint64)fileInfo.nFileSizeHigh << 32) |
+ (Uint64)fileInfo.nFileSizeLow);
+ }
+ return 1;
+}
+
+int
+efile_may_openfile(Efile_error* errInfo, char *name) {
+ DWORD attr;
+
+ if ((attr = GetFileAttributes(name)) == INVALID_FILE_ATTRIBUTES) {
+ return check_error(-1, errInfo);
+ }
+
+ if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+ errno = EISDIR;
+ return check_error(-1, errInfo);
+ }
+ return 1;
+#if 0
+ struct stat statbuf;
+
+ if (stat(name, &statbuf)) {
+ return check_error(-1, errInfo);
+ }
+ if (ISDIR(statbuf)) {
+ errno = EISDIR;
+ return check_error(-1, errInfo);
+ }
+ return 1;
+#endif
+}
+
+void
+efile_closefile(fd)
+int fd; /* File descriptor for file to close. */
+{
+ CloseHandle((HANDLE) fd);
+}
+
+int
+efile_fsync(errInfo, fd)
+Efile_error* errInfo; /* Where to return error codes. */
+int fd; /* File descriptor for file to sync. */
+{
+ if (!FlushFileBuffers((HANDLE) fd)) {
+ return check_error(-1, errInfo);
+ }
+ return 1;
+}
+
+int
+efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
+ char* orig_name, int info_for_link)
+{
+ HANDLE findhandle; /* Handle returned by FindFirstFile(). */
+ WIN32_FIND_DATA findbuf; /* Data return by FindFirstFile(). */
+ char name[_MAX_PATH];
+ int name_len;
+ char* path;
+ char pathbuf[_MAX_PATH];
+ int drive; /* Drive for filename (1 = A:, 2 = B: etc). */
+
+ /* Don't allow wildcards to be interpreted by system */
+
+ if (strpbrk(orig_name, "?*")) {
+ enoent:
+ errInfo->posix_errno = ENOENT;
+ errInfo->os_errno = ERROR_FILE_NOT_FOUND;
+ return 0;
+ }
+
+ /*
+ * Move the name to a buffer and make sure to remove a trailing
+ * slash, because it causes FindFirstFile() to fail on Win95.
+ */
+
+ if ((name_len = strlen(orig_name)) >= _MAX_PATH) {
+ goto enoent;
+ } else {
+ strcpy(name, orig_name);
+ if (name_len > 2 && ISSLASH(name[name_len-1]) &&
+ name[name_len-2] != ':') {
+ name[name_len-1] = '\0';
+ }
+ }
+
+ /* Try to get disk from name. If none, get current disk. */
+
+ if (name[1] != ':') {
+ drive = 0;
+ if (GetCurrentDirectory(sizeof(pathbuf), pathbuf) &&
+ pathbuf[1] == ':') {
+ drive = tolower(pathbuf[0]) - 'a' + 1;
+ }
+ } else if (*name && name[2] == '\0') {
+ /*
+ * X: and nothing more is an error.
+ */
+ errInfo->posix_errno = ENOENT;
+ errInfo->os_errno = ERROR_FILE_NOT_FOUND;
+ return 0;
+ } else
+ drive = tolower(*name) - 'a' + 1;
+
+ findhandle = FindFirstFile(name, &findbuf);
+ if (findhandle == INVALID_HANDLE_VALUE) {
+ if (!(strpbrk(name, "./\\") &&
+ (path = _fullpath(pathbuf, name, _MAX_PATH)) &&
+ /* root dir. ('C:\') or UNC root dir. ('\\server\share\') */
+ ((strlen(path) == 3) || IsRootUNCName(path)) &&
+ (GetDriveType(path) > 1) ) ) {
+ errInfo->posix_errno = ENOENT;
+ errInfo->os_errno = ERROR_FILE_NOT_FOUND;
+ return 0;
+ }
+
+ /*
+ * Root directories (such as C:\ or \\server\share\ are fabricated.
+ */
+
+ findbuf.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
+ findbuf.nFileSizeHigh = 0;
+ findbuf.nFileSizeLow = 0;
+ findbuf.cFileName[0] = '\0';
+
+ pInfo->modifyTime.year = 1980;
+ pInfo->modifyTime.month = 1;
+ pInfo->modifyTime.day = 1;
+ pInfo->modifyTime.hour = 0;
+ pInfo->modifyTime.minute = 0;
+ pInfo->modifyTime.second = 0;
+
+ pInfo->accessTime = pInfo->modifyTime;
+ } else {
+ SYSTEMTIME SystemTime;
+ FILETIME LocalFTime;
+
+#define GET_TIME(dst, src) \
+if (!FileTimeToLocalFileTime(&findbuf.src, &LocalFTime) || \
+ !FileTimeToSystemTime(&LocalFTime, &SystemTime)) { \
+ return set_error(errInfo); \
+} \
+(dst).year = SystemTime.wYear; \
+(dst).month = SystemTime.wMonth; \
+(dst).day = SystemTime.wDay; \
+(dst).hour = SystemTime.wHour; \
+(dst).minute = SystemTime.wMinute; \
+(dst).second = SystemTime.wSecond;
+
+ GET_TIME(pInfo->modifyTime, ftLastWriteTime);
+
+ if (findbuf.ftLastAccessTime.dwLowDateTime == 0 &&
+ findbuf.ftLastAccessTime.dwHighDateTime == 0) {
+ pInfo->accessTime = pInfo->modifyTime;
+ } else {
+ GET_TIME(pInfo->accessTime, ftLastAccessTime);
+ }
+
+ if (findbuf.ftCreationTime.dwLowDateTime == 0 &&
+ findbuf.ftCreationTime.dwHighDateTime == 0) {
+ pInfo->cTime = pInfo->modifyTime;
+ } else {
+ GET_TIME(pInfo->cTime, ftCreationTime);
+ }
+#undef GET_TIME
+ FindClose(findhandle);
+ }
+
+ pInfo->size_low = findbuf.nFileSizeLow;
+ pInfo->size_high = findbuf.nFileSizeHigh;
+
+ if (findbuf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ pInfo->type = FT_DIRECTORY;
+ else
+ pInfo->type = FT_REGULAR;
+
+ if (findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+ pInfo->access = FA_READ;
+ else
+ pInfo->access = FA_READ|FA_WRITE;
+
+ pInfo->mode = dos_to_posix_mode(findbuf.dwFileAttributes, name);
+ pInfo->links = 1;
+ pInfo->major_device = drive;
+ pInfo->minor_device = 0;
+ pInfo->inode = 0;
+ pInfo->uid = 0;
+ pInfo->gid = 0;
+
+ return 1;
+}
+
+int
+efile_write_info(errInfo, pInfo, name)
+Efile_error* errInfo;
+Efile_info* pInfo;
+char* name;
+{
+ SYSTEMTIME timebuf;
+ FILETIME LocalFileTime;
+ FILETIME ModifyFileTime;
+ FILETIME AccessFileTime;
+ FILETIME CreationFileTime;
+ HANDLE fd;
+ FILETIME* mtime = NULL;
+ FILETIME* atime = NULL;
+ FILETIME* ctime = NULL;
+ DWORD attr;
+ DWORD tempAttr;
+ BOOL modifyTime = FALSE;
+
+ /*
+ * Get the attributes for the file.
+ */
+
+ tempAttr = attr = GetFileAttributes((LPTSTR)name);
+ if (attr == 0xffffffff) {
+ return set_error(errInfo);
+ }
+ if (pInfo->mode != -1) {
+ if (pInfo->mode & _S_IWRITE) {
+ /* clear read only bit */
+ attr &= ~FILE_ATTRIBUTE_READONLY;
+ } else {
+ /* set read only bit */
+ attr |= FILE_ATTRIBUTE_READONLY;
+ }
+ }
+
+ /*
+ * Construct all file times.
+ */
+
+#define MKTIME(tb, ts, ptr) \
+ timebuf.wYear = ts.year; \
+ timebuf.wMonth = ts.month; \
+ timebuf.wDay = ts.day; \
+ timebuf.wHour = ts.hour; \
+ timebuf.wMinute = ts.minute; \
+ timebuf.wSecond = ts.second; \
+ timebuf.wMilliseconds = 0; \
+ if (ts.year != -1) { \
+ modifyTime = TRUE; \
+ ptr = &tb; \
+ if (!SystemTimeToFileTime(&timebuf, &LocalFileTime ) || \
+ !LocalFileTimeToFileTime(&LocalFileTime, &tb)) { \
+ errno = EINVAL; \
+ return check_error(-1, errInfo); \
+ } \
+ }
+
+ MKTIME(ModifyFileTime, pInfo->accessTime, mtime);
+ MKTIME(AccessFileTime, pInfo->modifyTime, atime);
+ MKTIME(CreationFileTime, pInfo->cTime, ctime);
+#undef MKTIME
+
+ /*
+ * If necessary, set the file times.
+ */
+
+ if (modifyTime) {
+ /*
+ * If the has read only access, we must temporarily turn on
+ * write access (this is necessary for native filesystems,
+ * but not for NFS filesystems).
+ */
+
+ if (tempAttr & FILE_ATTRIBUTE_READONLY) {
+ tempAttr &= ~FILE_ATTRIBUTE_READONLY;
+ if (!SetFileAttributes((LPTSTR) name, tempAttr)) {
+ return set_error(errInfo);
+ }
+ }
+
+ fd = CreateFile(name, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (fd != INVALID_HANDLE_VALUE) {
+ BOOL result = SetFileTime(fd, ctime, atime, mtime);
+ if (!result) {
+ return set_error(errInfo);
+ }
+ CloseHandle(fd);
+ }
+ }
+
+ /*
+ * If the file doesn't have the correct attributes, set them now.
+ * (It could have been done before setting the file times, above).
+ */
+
+ if (tempAttr != attr) {
+ if (!SetFileAttributes((LPTSTR) name, attr)) {
+ return set_error(errInfo);
+ }
+ }
+ return 1;
+}
+
+
+int
+efile_pwrite(errInfo, fd, buf, count, offset)
+Efile_error* errInfo; /* Where to return error codes. */
+int fd; /* File descriptor to write to. */
+char* buf; /* Buffer to write. */
+size_t count; /* Number of bytes to write. */
+Sint64 offset; /* where to write it */
+{
+ int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
+ if (res) {
+ return efile_write(errInfo, EFILE_MODE_WRITE, fd, buf, count);
+ } else {
+ return res;
+ }
+}
+
+/* position and read/write as a single atomic op */
+int
+efile_pread(errInfo, fd, offset, buf, count, pBytesRead)
+Efile_error* errInfo; /* Where to return error codes. */
+int fd; /* File descriptor to read from. */
+Sint64 offset; /* Offset in bytes from BOF. */
+char* buf; /* Buffer to read into. */
+size_t count; /* Number of bytes to read. */
+size_t* pBytesRead; /* Where to return number of bytes read. */
+{
+ int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
+ if (res) {
+ return efile_read(errInfo, EFILE_MODE_READ, fd, buf, count, pBytesRead);
+ } else {
+ return res;
+ }
+}
+
+
+
+int
+efile_write(errInfo, flags, fd, buf, count)
+Efile_error* errInfo; /* Where to return error codes. */
+int flags; /* Flags given when file was opened. */
+int fd; /* File descriptor to write to. */
+char* buf; /* Buffer to write. */
+size_t count; /* Number of bytes to write. */
+{
+ DWORD written; /* Bytes written in last operation. */
+
+ if (flags & EFILE_MODE_APPEND) {
+ (void) SetFilePointer((HANDLE) fd, 0, NULL, FILE_END);
+ }
+ while (count > 0) {
+ if (!WriteFile((HANDLE) fd, buf, count, &written, NULL))
+ return set_error(errInfo);
+ buf += written;
+ count -= written;
+ }
+ return 1;
+}
+
+int
+efile_writev(Efile_error* errInfo, /* Where to return error codes */
+ int flags, /* Flags given when file was
+ * opened */
+ int fd, /* File descriptor to write to */
+ SysIOVec* iov, /* Vector of buffer structs.
+ * The structs are unchanged
+ * after the call */
+ int iovcnt, /* Number of structs in vector */
+ size_t size) /* Number of bytes to write */
+{
+ int cnt; /* Buffers so far written */
+
+ ASSERT(iovcnt >= 0);
+
+ if (flags & EFILE_MODE_APPEND) {
+ (void) SetFilePointer((HANDLE) fd, 0, NULL, FILE_END);
+ }
+ for (cnt = 0; cnt < iovcnt; cnt++) {
+ if (iov[cnt].iov_base && iov[cnt].iov_len > 0) {
+ /* Non-empty buffer */
+ int p; /* Position in buffer */
+ int w = iov[cnt].iov_len;/* Bytes written in this call */
+ for (p = 0; p < iov[cnt].iov_len; p += w) {
+ if (!WriteFile((HANDLE) fd,
+ iov[cnt].iov_base + p,
+ iov[cnt].iov_len - p,
+ &w,
+ NULL))
+ return set_error(errInfo);
+ }
+ }
+ }
+ return 1;
+}
+
+int
+efile_read(errInfo, flags, fd, buf, count, pBytesRead)
+Efile_error* errInfo; /* Where to return error codes. */
+int flags; /* Flags given when file was opened. */
+int fd; /* File descriptor to read from. */
+char* buf; /* Buffer to read into. */
+size_t count; /* Number of bytes to read. */
+size_t* pBytesRead; /* Where to return number of bytes read. */
+{
+ if (!ReadFile((HANDLE) fd, buf, count, (DWORD *) pBytesRead, NULL))
+ return set_error(errInfo);
+ return 1;
+}
+
+int
+efile_seek(errInfo, fd, offset, origin, new_location)
+Efile_error* errInfo; /* Where to return error codes. */
+int fd; /* File descriptor to do the seek on. */
+Sint64 offset; /* Offset in bytes from the given origin. */
+int origin; /* Origin of seek (SEEK_SET, SEEK_CUR,
+ * SEEK_END).
+ */
+Sint64* new_location; /* Resulting new location in file. */
+{
+ LARGE_INTEGER off, new_loc;
+
+ switch (origin) {
+ case EFILE_SEEK_SET: origin = FILE_BEGIN; break;
+ case EFILE_SEEK_CUR: origin = FILE_CURRENT; break;
+ case EFILE_SEEK_END: origin = FILE_END; break;
+ default:
+ errno = EINVAL;
+ check_error(-1, errInfo);
+ break;
+ }
+
+ off.QuadPart = offset;
+ if (! SetFilePointerEx((HANDLE) fd, off,
+ new_location ? &new_loc : NULL, origin)) {
+ return set_error(errInfo);
+ }
+ if (new_location) {
+ *new_location = new_loc.QuadPart;
+ DEBUGF(("efile_seek(offset=%ld, origin=%d) -> %ld\n",
+ (long) offset, origin, (long) *new_location));
+ } else {
+ DEBUGF(("efile_seek(offset=%ld, origin=%d)\n", (long) offset, origin));
+ }
+ return 1;
+}
+
+int
+efile_truncate_file(errInfo, fd, flags)
+Efile_error* errInfo; /* Where to return error codes. */
+int *fd; /* File descriptor for file to truncate. */
+int flags;
+{
+ if (!SetEndOfFile((HANDLE) (*fd)))
+ return set_error(errInfo);
+ return 1;
+}
+
+
+/*
+ * IsRootUNCName - returns TRUE if the argument is a UNC name specifying
+ * a root share. That is, if it is of the form \\server\share\.
+ * This routine will also return true if the argument is of the
+ * form \\server\share (no trailing slash) but Win32 currently
+ * does not like that form.
+ *
+ * Forward slashes ('/') may be used instead of backslashes ('\').
+ */
+
+static int
+IsRootUNCName(const char* path)
+{
+ /*
+ * If a root UNC name, path will start with 2 (but not 3) slashes
+ */
+
+ if ((strlen(path) >= 5) /* minimum string is "//x/y" */
+ && ISSLASH(path[0]) && ISSLASH(path[1]))
+ {
+ const char * p = path + 2 ;
+
+ /*
+ * find the slash between the server name and share name
+ */
+ while ( * ++ p )
+ if ( ISSLASH(*p) )
+ break ;
+
+ if ( *p && p[1] )
+ {
+ /*
+ * is there a further slash?
+ */
+ while ( * ++ p )
+ if ( ISSLASH(*p) )
+ break ;
+
+ /*
+ * just final slash (or no final slash)
+ */
+ if ( !*p || !p[1])
+ return 1;
+ }
+ }
+
+ return 0 ;
+}
+
+/*
+ * Extracts the root part of an absolute filename (by modifying the string
+ * pointed to by the name argument). The name can start
+ * with either a driver letter (for example, C:\), or a UNC name
+ * (for example, \\guinness\bjorn).
+ *
+ * If the name is invalid, the buffer will be modified to point to
+ * an empty string.
+ *
+ * Returns: 1 if the name consists of just the root part, 0 if
+ * the name was longer.
+ */
+
+static int
+extract_root(char* name)
+{
+ int len = strlen(name);
+
+ if (isalpha(name[0]) && name[1] == ':' && ISSLASH(name[2])) {
+ int c = name[3];
+ name[3] = '\0';
+ return c == '\0';
+ } else if (len < 5 || !ISSLASH(name[0]) || !ISSLASH(name[1])) {
+ goto error;
+ } else { /* Try to find the end of the UNC name. */
+ char* p;
+ int c;
+
+ /*
+ * Find the slash between the server name and share name.
+ */
+
+ for (p = name + 2; *p; p++)
+ if (ISSLASH(*p))
+ break;
+ if (*p == '\0')
+ goto error;
+
+ /*
+ * Find the slash after the share name.
+ */
+
+ for (p++; *p; p++)
+ if (ISSLASH(*p))
+ break;
+ c = *p;
+ *p = '\0';
+ return c == '\0' || p[1] == '\0';
+ }
+
+ error:
+ *name = '\0';
+ return 1;
+}
+
+static unsigned short
+dos_to_posix_mode(int attr, const char *name)
+{
+ register unsigned short uxmode;
+ unsigned dosmode;
+ register const char *p;
+
+ dosmode = attr & 0xff;
+ if ((p = name)[1] == ':')
+ p += 2;
+
+ /* check to see if this is a directory - note we must make a special
+ * check for the root, which DOS thinks is not a directory
+ */
+
+ uxmode = (unsigned short)
+ (((ISSLASH(*p) && !p[1]) || (dosmode & FILE_ATTRIBUTE_DIRECTORY) ||
+ *p == '\0') ? _S_IFDIR|_S_IEXEC : _S_IFREG);
+
+ /* If attribute byte does not have read-only bit, it is read-write */
+
+ uxmode |= (dosmode & FILE_ATTRIBUTE_READONLY) ?
+ _S_IREAD : (_S_IREAD|_S_IWRITE);
+
+ /* see if file appears to be executable - check extension of name */
+
+ if (p = strrchr(name, '.')) {
+ if (!stricmp(p, ".exe") ||
+ !stricmp(p, ".cmd") ||
+ !stricmp(p, ".bat") ||
+ !stricmp(p, ".com"))
+ uxmode |= _S_IEXEC;
+ }
+
+ /* propagate user read/write/execute bits to group/other fields */
+
+ uxmode |= (uxmode & 0700) >> 3;
+ uxmode |= (uxmode & 0700) >> 6;
+
+ return uxmode;
+}
+
+int
+efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
+{
+ errno = ENOTSUP;
+ return check_error(-1, errInfo);
+}
+
+
+int
+efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size)
+{
+ WIN32_FIND_DATA wfd;
+ HANDLE fh;
+ char name[_MAX_PATH];
+ int name_len;
+ char* path;
+ char pathbuf[_MAX_PATH];
+ int drive; /* Drive for filename (1 = A:, 2 = B: etc). */
+
+ /* Don't allow wildcards to be interpreted by system */
+
+ if (strpbrk(orig_name, "?*")) {
+ enoent:
+ errInfo->posix_errno = ENOENT;
+ errInfo->os_errno = ERROR_FILE_NOT_FOUND;
+ return 0;
+ }
+
+ /*
+ * Move the name to a buffer and make sure to remove a trailing
+ * slash, because it causes FindFirstFile() to fail on Win95.
+ */
+
+ if ((name_len = strlen(orig_name)) >= _MAX_PATH) {
+ goto enoent;
+ } else {
+ strcpy(name, orig_name);
+ if (name_len > 2 && ISSLASH(name[name_len-1]) &&
+ name[name_len-2] != ':') {
+ name[name_len-1] = '\0';
+ }
+ }
+
+ /* Try to get disk from name. If none, get current disk. */
+
+ if (name[1] != ':') {
+ drive = 0;
+ if (GetCurrentDirectory(sizeof(pathbuf), pathbuf) &&
+ pathbuf[1] == ':') {
+ drive = tolower(pathbuf[0]) - 'a' + 1;
+ }
+ } else if (*name && name[2] == '\0') {
+ /*
+ * X: and nothing more is an error.
+ */
+ goto enoent;
+ } else {
+ drive = tolower(*name) - 'a' + 1;
+ }
+ fh = FindFirstFile(name,&wfd);
+ if (fh == INVALID_HANDLE_VALUE) {
+ if (!(strpbrk(name, "./\\") &&
+ (path = _fullpath(pathbuf, name, _MAX_PATH)) &&
+ /* root dir. ('C:\') or UNC root dir. ('\\server\share\') */
+ ((strlen(path) == 3) || IsRootUNCName(path)) &&
+ (GetDriveType(path) > 1) ) ) {
+ errno = errno_map(GetLastError());
+ return check_error(-1, errInfo);
+ }
+ /*
+ * Root directories (such as C:\ or \\server\share\ are fabricated.
+ */
+ strcpy(buffer,name);
+ return 1;
+ }
+
+ strcpy(buffer,wfd.cAlternateFileName);
+ if (!*buffer) {
+ strcpy(buffer,wfd.cFileName);
+ }
+
+ return 1;
+}
+
+int
+efile_link(Efile_error* errInfo, char* old, char* new)
+{
+ errno = ENOTSUP;
+ return check_error(-1, errInfo);
+}
+
+int
+efile_symlink(Efile_error* errInfo, char* old, char* new)
+{
+ errno = ENOTSUP;
+ return check_error(-1, errInfo);
+}
diff --git a/erts/emulator/drivers/win32/winsock_func.h b/erts/emulator/drivers/win32/winsock_func.h
new file mode 100644
index 0000000000..9d2c099c4d
--- /dev/null
+++ b/erts/emulator/drivers/win32/winsock_func.h
@@ -0,0 +1,102 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+typedef struct _WinSockFuncs {
+ int (WSAAPI *WSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData);
+ int (WSAAPI *WSACleanup)(void);
+ int (WSAAPI *WSAGetLastError)(void);
+ DWORD (WSAAPI *WSAWaitForMultipleEvents) (DWORD cEvents,
+ const WSAEVENT FAR * lphEvents,
+ BOOL fWaitAll,
+ DWORD dwTimeout,
+ BOOL fAlertable);
+ WSAEVENT (WSAAPI *WSACreateEvent)(void);
+ BOOL (WSAAPI *WSACloseEvent)(WSAEVENT hEvent);
+
+ BOOL (WSAAPI *WSASetEvent)(WSAEVENT hEvent);
+ BOOL (WSAAPI *WSAResetEvent)(WSAEVENT hEvent);
+ int (WSAAPI *WSAEventSelect)(SOCKET s, WSAEVENT hEventObject,
+ long lNetworkEvents);
+ int (WSAAPI *WSAEnumNetworkEvents)(SOCKET s,
+ WSAEVENT hEventObject,
+ LPWSANETWORKEVENTS lpNetworkEvents);
+ int (WSAAPI *WSAIoctl)(SOCKET s,
+ DWORD dwIoControlCode,
+ LPVOID lpvInBuffer,
+ DWORD cbInBuffer,
+ LPVOID lpvOUTBuffer,
+ DWORD cbOUTBuffer,
+ LPDWORD lpcbBytesReturned,
+ LPWSAOVERLAPPED lpOverlapped,
+ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE
+ );
+ SOCKET (WSAAPI *accept)(SOCKET s, struct sockaddr FAR *addr,
+ int FAR *addrlen);
+ int (WSAAPI *bind)(SOCKET s, const struct sockaddr FAR *addr,
+ int namelen);
+ int (WSAAPI *closesocket)(SOCKET s);
+ int (WSAAPI *connect)(SOCKET s, const struct sockaddr FAR *name,
+ int namelen);
+ int (WSAAPI *ioctlsocket)(SOCKET s, long cmd, u_long FAR *argp);
+ int (WSAAPI *getsockopt)(SOCKET s, int level, int optname,
+ char FAR * optval, int FAR *optlen);
+ u_long (WSAAPI *htonl)(u_long hostlong);
+ u_short (WSAAPI *htons)(u_short hostshort);
+ unsigned long (WSAAPI *inet_addr)(const char FAR * cp);
+ char FAR * (WSAAPI *inet_ntoa)(struct in_addr in);
+ int (WSAAPI *listen)(SOCKET s, int backlog);
+ u_short (WSAAPI *ntohs)(u_short netshort);
+ int (WSAAPI *recv)(SOCKET s, char FAR * buf, int len, int flags);
+ int (WSAAPI *send)(SOCKET s, const char FAR * buf, int len, int flags);
+ int (WSAAPI *setsockopt)(SOCKET s, int level, int optname,
+ const char FAR * optval, int optlen);
+ int (WSAAPI *shutdown)(SOCKET s, int how);
+ SOCKET (WSAAPI *socket)(int af, int type, int protocol);
+ struct hostent FAR * (WSAAPI *gethostbyname)(const char FAR * name);
+ struct hostent FAR * (WSAAPI *gethostbyaddr)(const char FAR *addr,
+ int addrlen, int addrtype);
+ int (WSAAPI *gethostname)(char FAR * name, int namelen);
+ struct servent FAR * (WSAAPI *getservbyname)(const char FAR * name,
+ const char FAR * proto);
+ struct servent FAR * (WSAAPI *getservbyport)(int port,
+ const char FAR * proto);
+ int (WSAAPI *getsockname)(SOCKET sock, struct sockaddr FAR *name,
+ int FAR *namelen);
+
+ /*
+ * New, added for inet_drv.
+ */
+
+ int (WSAAPI *getpeername)(SOCKET s, struct sockaddr FAR * name,
+ int FAR * namelen);
+ u_long (WSAAPI *ntohl)(u_long netlong);
+ int (WSAAPI *WSASend)(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
+ LPDWORD lpNumberOfBytesSent, DWORD dwFlags,
+ LPWSAOVERLAPPED lpOverlapped,
+ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
+ int (WSAAPI *sendto)(SOCKET s, const char FAR * buf, int len,
+ int flags, const struct sockaddr FAR * to, int tolen);
+ int (WSAAPI *recvfrom)(SOCKET s, char FAR * buf, int len, int flags,
+ struct sockaddr FAR * from, int FAR * fromlen);
+} WinSockFuncs;
+
+
+extern WinSockFuncs winSock;
+
+extern int tcp_lookup_functions(void);