diff src/os_win32.c @ 7:3fc0f57ecb91 v7.0001

updated for version 7.0001
author vimboss
date Sun, 13 Jun 2004 20:20:40 +0000
parents
children 4102fb4ea781
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/os_win32.c
@@ -0,0 +1,4790 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved	by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+/*
+ * os_win32.c
+ *
+ * Used for both the console version and the Win32 GUI.  A lot of code is for
+ * the console version only, so there is a lot of "#ifndef FEAT_GUI_W32".
+ *
+ * Win32 (Windows NT and Windows 95) system-dependent routines.
+ * Portions lifted from the Win32 SDK samples, the MSDOS-dependent code,
+ * NetHack 3.1.3, GNU Emacs 19.30, and Vile 5.5.
+ *
+ * George V. Reilly <george@reilly.org> wrote most of this.
+ * Roger Knobbe <rogerk@wonderware.com> did the initial port of Vim 3.0.
+ */
+
+#include <io.h>
+#include "vim.h"
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#include <sys/types.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <process.h>
+
+#undef chdir
+#ifdef __GNUC__
+# ifndef __MINGW32__
+#  include <dirent.h>
+# endif
+#else
+# include <direct.h>
+#endif
+
+#if defined(FEAT_TITLE) && !defined(FEAT_GUI_W32)
+# include <shellapi.h>
+#endif
+
+#ifdef __MINGW32__
+# ifndef FROM_LEFT_1ST_BUTTON_PRESSED
+#  define FROM_LEFT_1ST_BUTTON_PRESSED    0x0001
+# endif
+# ifndef RIGHTMOST_BUTTON_PRESSED
+#  define RIGHTMOST_BUTTON_PRESSED	  0x0002
+# endif
+# ifndef FROM_LEFT_2ND_BUTTON_PRESSED
+#  define FROM_LEFT_2ND_BUTTON_PRESSED    0x0004
+# endif
+# ifndef FROM_LEFT_3RD_BUTTON_PRESSED
+#  define FROM_LEFT_3RD_BUTTON_PRESSED    0x0008
+# endif
+# ifndef FROM_LEFT_4TH_BUTTON_PRESSED
+#  define FROM_LEFT_4TH_BUTTON_PRESSED    0x0010
+# endif
+
+/*
+ * EventFlags
+ */
+# ifndef MOUSE_MOVED
+#  define MOUSE_MOVED   0x0001
+# endif
+# ifndef DOUBLE_CLICK
+#  define DOUBLE_CLICK  0x0002
+# endif
+#endif
+
+/* Record all output and all keyboard & mouse input */
+/* #define MCH_WRITE_DUMP */
+
+#ifdef MCH_WRITE_DUMP
+FILE* fdDump = NULL;
+#endif
+
+/*
+ * When generating prototypes for Win32 on Unix, these lines make the syntax
+ * errors disappear.  They do not need to be correct.
+ */
+#ifdef PROTO
+#define WINAPI
+#define WINBASEAPI
+typedef char * LPCSTR;
+typedef int ACCESS_MASK;
+typedef int BOOL;
+typedef int COLORREF;
+typedef int CONSOLE_CURSOR_INFO;
+typedef int COORD;
+typedef int DWORD;
+typedef int HANDLE;
+typedef int HDC;
+typedef int HFONT;
+typedef int HICON;
+typedef int HINSTANCE;
+typedef int HWND;
+typedef int INPUT_RECORD;
+typedef int KEY_EVENT_RECORD;
+typedef int LOGFONT;
+typedef int LPBOOL;
+typedef int LPCTSTR;
+typedef int LPDWORD;
+typedef int LPSTR;
+typedef int LPTSTR;
+typedef int LPVOID;
+typedef int MOUSE_EVENT_RECORD;
+typedef int PACL;
+typedef int PDWORD;
+typedef int PHANDLE;
+typedef int PRINTDLG;
+typedef int PSECURITY_DESCRIPTOR;
+typedef int PSID;
+typedef int SECURITY_INFORMATION;
+typedef int SHORT;
+typedef int SMALL_RECT;
+typedef int TEXTMETRIC;
+typedef int TOKEN_INFORMATION_CLASS;
+typedef int TRUSTEE;
+typedef int WORD;
+typedef int WCHAR;
+typedef void VOID;
+#endif
+
+#ifndef FEAT_GUI_W32
+/* Undocumented API in kernel32.dll needed to work around dead key bug in
+ * console-mode applications in NT 4.0.  If you switch keyboard layouts
+ * in a console app to a layout that includes dead keys and then hit a
+ * dead key, a call to ToAscii will trash the stack.  My thanks to Ian James
+ * and Michael Dietrich for helping me figure out this workaround.
+ */
+
+/* WINBASEAPI BOOL WINAPI GetConsoleKeyboardLayoutNameA(LPSTR); */
+#ifndef WINBASEAPI
+# define WINBASEAPI __stdcall
+#endif
+#if defined(__BORLANDC__)
+typedef BOOL (__stdcall *PFNGCKLN)(LPSTR);
+#else
+typedef WINBASEAPI BOOL (WINAPI *PFNGCKLN)(LPSTR);
+#endif
+PFNGCKLN    s_pfnGetConsoleKeyboardLayoutName = NULL;
+#endif
+
+#if defined(__BORLANDC__)
+/* Strangely Borland uses a non-standard name. */
+# define wcsicmp(a, b) wcscmpi((a), (b))
+#endif
+
+#ifndef FEAT_GUI_W32
+/* Win32 Console handles for input and output */
+static HANDLE g_hConIn  = INVALID_HANDLE_VALUE;
+static HANDLE g_hConOut = INVALID_HANDLE_VALUE;
+
+/* Win32 Screen buffer,coordinate,console I/O information */
+static SMALL_RECT g_srScrollRegion;
+static COORD	  g_coord;  /* 0-based, but external coords are 1-based */
+
+/* The attribute of the screen when the editor was started */
+static WORD  g_attrDefault = 7;  /* lightgray text on black background */
+static WORD  g_attrCurrent;
+
+static int g_fCBrkPressed = FALSE;  /* set by ctrl-break interrupt */
+static int g_fCtrlCPressed = FALSE; /* set when ctrl-C or ctrl-break detected */
+static int g_fForceExit = FALSE;    /* set when forcefully exiting */
+
+static void termcap_mode_start(void);
+static void termcap_mode_end(void);
+static void clear_chars(COORD coord, DWORD n);
+static void clear_screen(void);
+static void clear_to_end_of_display(void);
+static void clear_to_end_of_line(void);
+static void scroll(unsigned cLines);
+static void set_scroll_region(unsigned left, unsigned top,
+			      unsigned right, unsigned bottom);
+static void insert_lines(unsigned cLines);
+static void delete_lines(unsigned cLines);
+static void gotoxy(unsigned x, unsigned y);
+static void normvideo(void);
+static void textattr(WORD wAttr);
+static void textcolor(WORD wAttr);
+static void textbackground(WORD wAttr);
+static void standout(void);
+static void standend(void);
+static void visual_bell(void);
+static void cursor_visible(BOOL fVisible);
+static BOOL write_chars(LPCSTR pchBuf, DWORD cchToWrite);
+static char_u tgetch(int *pmodifiers, char_u *pch2);
+static void create_conin(void);
+static int s_cursor_visible = TRUE;
+static int did_create_conin = FALSE;
+#else
+static int s_dont_use_vimrun = TRUE;
+static int need_vimrun_warning = FALSE;
+static char *vimrun_path = "vimrun ";
+#endif
+
+#ifndef FEAT_GUI_W32
+static int suppress_winsize = 1;	/* don't fiddle with console */
+#endif
+
+    static void
+get_exe_name(void)
+{
+    char	temp[256];
+
+    if (exe_name == NULL)
+    {
+	/* store the name of the executable, may be used for $VIM */
+	GetModuleFileName(NULL, temp, 255);
+	if (*temp != NUL)
+	    exe_name = FullName_save((char_u *)temp, FALSE);
+    }
+}
+
+#if defined(DYNAMIC_GETTEXT) || defined(PROTO)
+# ifndef GETTEXT_DLL
+#  define GETTEXT_DLL "libintl.dll"
+# endif
+/* Dummy funcitons */
+static char* null_libintl_gettext(const char *);
+static char* null_libintl_textdomain(const char *);
+static char* null_libintl_bindtextdomain(const char *, const char *);
+
+static HINSTANCE hLibintlDLL = 0;
+char* (*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
+char* (*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
+char* (*dyn_libintl_bindtextdomain)(const char *, const char *)
+						= null_libintl_bindtextdomain;
+
+    int
+dyn_libintl_init(char *libname)
+{
+    int i;
+    static struct
+    {
+	char	    *name;
+	FARPROC	    *ptr;
+    } libintl_entry[] =
+    {
+	{"gettext", (FARPROC*)&dyn_libintl_gettext},
+	{"textdomain", (FARPROC*)&dyn_libintl_textdomain},
+	{"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
+	{NULL, NULL}
+    };
+
+    /* No need to initialize twice. */
+    if (hLibintlDLL)
+	return 1;
+    /* Load gettext library (libintl.dll) */
+    hLibintlDLL = LoadLibrary(libname != NULL ? libname : GETTEXT_DLL);
+    if (!hLibintlDLL)
+    {
+	char_u	    dirname[_MAX_PATH];
+
+	/* Try using the path from gvim.exe to find the .dll there. */
+	get_exe_name();
+	STRCPY(dirname, exe_name);
+	STRCPY(gettail(dirname), GETTEXT_DLL);
+	hLibintlDLL = LoadLibrary((char *)dirname);
+	if (!hLibintlDLL)
+	{
+	    if (p_verbose > 0)
+		EMSG2(_(e_loadlib), GETTEXT_DLL);
+	    return 0;
+	}
+    }
+    for (i = 0; libintl_entry[i].name != NULL
+					 && libintl_entry[i].ptr != NULL; ++i)
+    {
+	if ((*libintl_entry[i].ptr = (FARPROC)GetProcAddress(hLibintlDLL,
+					      libintl_entry[i].name)) == NULL)
+	{
+	    dyn_libintl_end();
+	    if (p_verbose > 0)
+		EMSG2(_(e_loadfunc), libintl_entry[i].name);
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+    void
+dyn_libintl_end()
+{
+    if (hLibintlDLL)
+	FreeLibrary(hLibintlDLL);
+    hLibintlDLL			= NULL;
+    dyn_libintl_gettext		= null_libintl_gettext;
+    dyn_libintl_textdomain	= null_libintl_textdomain;
+    dyn_libintl_bindtextdomain	= null_libintl_bindtextdomain;
+}
+
+    static char *
+null_libintl_gettext(const char* msgid)
+{
+    return (char*)msgid;
+}
+
+    static char *
+null_libintl_bindtextdomain(const char* domainname, const char* dirname)
+{
+    return NULL;
+}
+
+    static char *
+null_libintl_textdomain(const char* domainname)
+{
+    return NULL;
+}
+
+#endif /* DYNAMIC_GETTEXT */
+
+/* This symbol is not defined in older versions of the SDK or Visual C++ */
+
+#ifndef VER_PLATFORM_WIN32_WINDOWS
+# define VER_PLATFORM_WIN32_WINDOWS 1
+#endif
+
+DWORD g_PlatformId;
+
+#ifdef HAVE_ACL
+# include <aclapi.h>
+/*
+ * These are needed to dynamically load the ADVAPI DLL, which is not
+ * implemented under Windows 95 (and causes VIM to crash)
+ */
+typedef DWORD (WINAPI *PSNSECINFO) (LPTSTR, enum SE_OBJECT_TYPE,
+	SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
+typedef DWORD (WINAPI *PGNSECINFO) (LPSTR, enum SE_OBJECT_TYPE,
+	SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *,
+	PSECURITY_DESCRIPTOR *);
+
+static HANDLE advapi_lib = NULL;	/* Handle for ADVAPI library */
+static PSNSECINFO pSetNamedSecurityInfo;
+static PGNSECINFO pGetNamedSecurityInfo;
+#endif
+
+/*
+ * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
+ * VER_PLATFORM_WIN32_WINDOWS (Win95).
+ */
+    void
+PlatformId(void)
+{
+    static int done = FALSE;
+
+    if (!done)
+    {
+	OSVERSIONINFO ovi;
+
+	ovi.dwOSVersionInfoSize = sizeof(ovi);
+	GetVersionEx(&ovi);
+
+	g_PlatformId = ovi.dwPlatformId;
+
+#ifdef HAVE_ACL
+	/*
+	 * Load the ADVAPI runtime if we are on anything
+	 * other than Windows 95
+	 */
+	if (g_PlatformId == VER_PLATFORM_WIN32_NT)
+	{
+	    /*
+	     * do this load.  Problems: Doesn't unload at end of run (this is
+	     * theoretically okay, since Windows should unload it when VIM
+	     * terminates).  Should we be using the 'mch_libcall' routines?
+	     * Seems like a lot of overhead to load/unload ADVAPI32.DLL each
+	     * time we verify security...
+	     */
+	    advapi_lib = LoadLibrary("ADVAPI32.DLL");
+	    if (advapi_lib != NULL)
+	    {
+		pSetNamedSecurityInfo = (PSNSECINFO)GetProcAddress(advapi_lib,
+						      "SetNamedSecurityInfoA");
+		pGetNamedSecurityInfo = (PGNSECINFO)GetProcAddress(advapi_lib,
+						      "GetNamedSecurityInfoA");
+		if (pSetNamedSecurityInfo == NULL
+			|| pGetNamedSecurityInfo == NULL)
+		{
+		    /* If we can't get the function addresses, set advapi_lib
+		     * to NULL so that we don't use them. */
+		    FreeLibrary(advapi_lib);
+		    advapi_lib = NULL;
+		}
+	    }
+	}
+#endif
+	done = TRUE;
+    }
+}
+
+/*
+ * Return TRUE when running on Windows 95 (or 98 or ME).
+ * Only to be used after mch_init().
+ */
+    int
+mch_windows95(void)
+{
+    return g_PlatformId == VER_PLATFORM_WIN32_WINDOWS;
+}
+
+#ifdef FEAT_GUI_W32
+/*
+ * Used to work around the "can't do synchronous spawn"
+ * problem on Win32s, without resorting to Universal Thunk.
+ */
+static int old_num_windows;
+static int num_windows;
+
+    static BOOL CALLBACK
+win32ssynch_cb(HWND hwnd, LPARAM lparam)
+{
+    num_windows++;
+    return TRUE;
+}
+#endif
+
+#ifndef FEAT_GUI_W32
+
+#define SHIFT  (SHIFT_PRESSED)
+#define CTRL   (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
+#define ALT    (RIGHT_ALT_PRESSED  | LEFT_ALT_PRESSED)
+#define ALT_GR (RIGHT_ALT_PRESSED  | LEFT_CTRL_PRESSED)
+
+
+/* When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
+ * We map function keys to their ANSI terminal equivalents, as produced
+ * by ANSI.SYS, for compatibility with the MS-DOS version of Vim.  Any
+ * ANSI key with a value >= '\300' is nonstandard, but provided anyway
+ * so that the user can have access to all SHIFT-, CTRL-, and ALT-
+ * combinations of function/arrow/etc keys.
+ */
+
+const static struct
+{
+    WORD    wVirtKey;
+    BOOL    fAnsiKey;
+    int	    chAlone;
+    int	    chShift;
+    int	    chCtrl;
+    int	    chAlt;
+} VirtKeyMap[] =
+{
+
+/*    Key	ANSI	alone	shift	ctrl	    alt */
+    { VK_ESCAPE,FALSE,	ESC,	ESC,	ESC,	    ESC,    },
+
+    { VK_F1,	TRUE,	';',	'T',	'^',	    'h', },
+    { VK_F2,	TRUE,	'<',	'U',	'_',	    'i', },
+    { VK_F3,	TRUE,	'=',	'V',	'`',	    'j', },
+    { VK_F4,	TRUE,	'>',	'W',	'a',	    'k', },
+    { VK_F5,	TRUE,	'?',	'X',	'b',	    'l', },
+    { VK_F6,	TRUE,	'@',	'Y',	'c',	    'm', },
+    { VK_F7,	TRUE,	'A',	'Z',	'd',	    'n', },
+    { VK_F8,	TRUE,	'B',	'[',	'e',	    'o', },
+    { VK_F9,	TRUE,	'C',	'\\',	'f',	    'p', },
+    { VK_F10,	TRUE,	'D',	']',	'g',	    'q', },
+    { VK_F11,	TRUE,	'\205',	'\207',	'\211',	    '\213', },
+    { VK_F12,	TRUE,	'\206',	'\210',	'\212',	    '\214', },
+
+    { VK_HOME,	TRUE,	'G',	'\302',	'w',	    '\303', },
+    { VK_UP,	TRUE,	'H',	'\304',	'\305',	    '\306', },
+    { VK_PRIOR,	TRUE,	'I',	'\307',	'\204',	    '\310', }, /*PgUp*/
+    { VK_LEFT,	TRUE,	'K',	'\311',	's',	    '\312', },
+    { VK_RIGHT,	TRUE,	'M',	'\313',	't',	    '\314', },
+    { VK_END,	TRUE,	'O',	'\315',	'u',	    '\316', },
+    { VK_DOWN,	TRUE,	'P',	'\317',	'\320',	    '\321', },
+    { VK_NEXT,	TRUE,	'Q',	'\322',	'v',	    '\323', }, /*PgDn*/
+    { VK_INSERT,TRUE,	'R',	'\324',	'\325',	    '\326', },
+    { VK_DELETE,TRUE,	'S',	'\327',	'\330',	    '\331', },
+
+    { VK_SNAPSHOT,TRUE,	0,	0,	0,	    'r', }, /*PrtScrn*/
+
+#if 0
+    /* Most people don't have F13-F20, but what the hell... */
+    { VK_F13,	TRUE,	'\332',	'\333',	'\334',	    '\335', },
+    { VK_F14,	TRUE,	'\336',	'\337',	'\340',	    '\341', },
+    { VK_F15,	TRUE,	'\342',	'\343',	'\344',	    '\345', },
+    { VK_F16,	TRUE,	'\346',	'\347',	'\350',	    '\351', },
+    { VK_F17,	TRUE,	'\352',	'\353',	'\354',	    '\355', },
+    { VK_F18,	TRUE,	'\356',	'\357',	'\360',	    '\361', },
+    { VK_F19,	TRUE,	'\362',	'\363',	'\364',	    '\365', },
+    { VK_F20,	TRUE,	'\366',	'\367',	'\370',	    '\371', },
+#endif
+    { VK_ADD,	TRUE,   'N',    'N',    'N',	'N',	}, /* keyp '+' */
+    { VK_SUBTRACT, TRUE,'J',	'J',    'J',	'J',	}, /* keyp '-' */
+ /* { VK_DIVIDE,   TRUE,'N',	'N',    'N',	'N',	},    keyp '/' */
+    { VK_MULTIPLY, TRUE,'7',	'7',    '7',	'7',	}, /* keyp '*' */
+
+    { VK_NUMPAD0,TRUE,  '\332',	'\333',	'\334',	    '\335', },
+    { VK_NUMPAD1,TRUE,  '\336',	'\337',	'\340',	    '\341', },
+    { VK_NUMPAD2,TRUE,  '\342',	'\343',	'\344',	    '\345', },
+    { VK_NUMPAD3,TRUE,  '\346',	'\347',	'\350',	    '\351', },
+    { VK_NUMPAD4,TRUE,  '\352',	'\353',	'\354',	    '\355', },
+    { VK_NUMPAD5,TRUE,  '\356',	'\357',	'\360',	    '\361', },
+    { VK_NUMPAD6,TRUE,  '\362',	'\363',	'\364',	    '\365', },
+    { VK_NUMPAD7,TRUE,  '\366',	'\367',	'\370',	    '\371', },
+    { VK_NUMPAD8,TRUE,  '\372',	'\373',	'\374',	    '\375', },
+    /* Sorry, out of number space! <negri>*/
+    { VK_NUMPAD9,TRUE,  '\376',	'\377',	'\377',	    '\367', },
+
+};
+
+
+#ifdef _MSC_VER
+// The ToAscii bug destroys several registers.	Need to turn off optimization
+// or the GetConsoleKeyboardLayoutName hack will fail in non-debug versions
+# pragma optimize("", off)
+#endif
+
+#if defined(__GNUC__) && !defined(__MINGW32__)  && !defined(__CYGWIN__)
+# define AChar AsciiChar
+#else
+# define AChar uChar.AsciiChar
+#endif
+
+/* The return code indicates key code size. */
+    static int
+#ifdef __BORLANDC__
+    __stdcall
+#endif
+win32_kbd_patch_key(
+    KEY_EVENT_RECORD* pker)
+{
+    UINT uMods = pker->dwControlKeyState;
+    static int s_iIsDead = 0;
+    static WORD awAnsiCode[2];
+    static BYTE abKeystate[256];
+
+
+    if (s_iIsDead == 2)
+    {
+	pker->AChar = (CHAR) awAnsiCode[1];
+	s_iIsDead = 0;
+	return 1;
+    }
+
+    if (pker->AChar != 0)
+	return 1;
+
+    memset(abKeystate, 0, sizeof (abKeystate));
+
+    // Should only be non-NULL on NT 4.0
+    if (s_pfnGetConsoleKeyboardLayoutName != NULL)
+    {
+	CHAR szKLID[KL_NAMELENGTH];
+
+	if ((*s_pfnGetConsoleKeyboardLayoutName)(szKLID))
+	    (void)LoadKeyboardLayout(szKLID, KLF_ACTIVATE);
+    }
+
+    /* Clear any pending dead keys */
+    ToAscii(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 0);
+
+    if (uMods & SHIFT_PRESSED)
+	abKeystate[VK_SHIFT] = 0x80;
+    if (uMods & CAPSLOCK_ON)
+	abKeystate[VK_CAPITAL] = 1;
+
+    if ((uMods & ALT_GR) == ALT_GR)
+    {
+	abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
+	    abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
+    }
+
+    s_iIsDead = ToAscii(pker->wVirtualKeyCode, pker->wVirtualScanCode,
+			abKeystate, awAnsiCode, 0);
+
+    if (s_iIsDead > 0)
+	pker->AChar = (CHAR) awAnsiCode[0];
+
+    return s_iIsDead;
+}
+
+#ifdef _MSC_VER
+/* MUST switch optimization on again here, otherwise a call to
+ * decode_key_event() may crash (e.g. when hitting caps-lock) */
+# pragma optimize("", on)
+
+# if (_MSC_VER < 1100)
+/* MUST turn off global optimisation for this next function, or
+ * pressing ctrl-minus in insert mode crashes Vim when built with
+ * VC4.1. -- negri. */
+#  pragma optimize("g", off)
+# endif
+#endif
+
+static BOOL g_fJustGotFocus = FALSE;
+
+/*
+ * Decode a KEY_EVENT into one or two keystrokes
+ */
+    static BOOL
+decode_key_event(
+    KEY_EVENT_RECORD	*pker,
+    char_u		*pch,
+    char_u		*pch2,
+    int			*pmodifiers,
+    BOOL		fDoPost)
+{
+    int i;
+    const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
+
+    *pch = *pch2 = NUL;
+    g_fJustGotFocus = FALSE;
+
+    /* ignore key up events */
+    if (!pker->bKeyDown)
+	return FALSE;
+
+    /* ignore some keystrokes */
+    switch (pker->wVirtualKeyCode)
+    {
+    /* modifiers */
+    case VK_SHIFT:
+    case VK_CONTROL:
+    case VK_MENU:   /* Alt key */
+	return FALSE;
+
+    default:
+	break;
+    }
+
+    /* special cases */
+    if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->AChar == NUL)
+    {
+	/* Ctrl-6 is Ctrl-^ */
+	if (pker->wVirtualKeyCode == '6')
+	{
+	    *pch = Ctrl_HAT;
+	    return TRUE;
+	}
+	/* Ctrl-2 is Ctrl-@ */
+	else if (pker->wVirtualKeyCode == '2')
+	{
+	    *pch = NUL;
+	    return TRUE;
+	}
+	/* Ctrl-- is Ctrl-_ */
+	else if (pker->wVirtualKeyCode == 0xBD)
+	{
+	    *pch = Ctrl__;
+	    return TRUE;
+	}
+    }
+
+    /* Shift-TAB */
+    if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED))
+    {
+	*pch = K_NUL;
+	*pch2 = '\017';
+	return TRUE;
+    }
+
+    for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]);  --i >= 0;  )
+    {
+	if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
+	{
+	    if (nModifs == 0)
+		*pch = VirtKeyMap[i].chAlone;
+	    else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
+		*pch = VirtKeyMap[i].chShift;
+	    else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
+		*pch = VirtKeyMap[i].chCtrl;
+	    else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
+		*pch = VirtKeyMap[i].chAlt;
+
+	    if (*pch != 0)
+	    {
+		if (VirtKeyMap[i].fAnsiKey)
+		{
+		    *pch2 = *pch;
+		    *pch = K_NUL;
+		}
+
+		return TRUE;
+	    }
+	}
+    }
+
+    i = win32_kbd_patch_key(pker);
+
+    if (i < 0)
+	*pch = NUL;
+    else
+    {
+	*pch = (i > 0) ? pker->AChar : NUL;
+
+	if (pmodifiers != NULL)
+	{
+	    /* Pass on the ALT key as a modifier, but only when not combined
+	     * with CTRL (which is ALTGR). */
+	    if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0)
+		*pmodifiers |= MOD_MASK_ALT;
+
+	    /* Pass on SHIFT only for special keys, because we don't know when
+	     * it's already included with the character. */
+	    if ((nModifs & SHIFT) != 0 && *pch <= 0x20)
+		*pmodifiers |= MOD_MASK_SHIFT;
+
+	    /* Pass on CTRL only for non-special keys, because we don't know
+	     * when it's already included with the character.  And not when
+	     * combined with ALT (which is ALTGR). */
+	    if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0
+					       && *pch >= 0x20 && *pch < 0x80)
+		*pmodifiers |= MOD_MASK_CTRL;
+	}
+    }
+
+    return (*pch != NUL);
+}
+
+#ifdef _MSC_VER
+# pragma optimize("", on)
+#endif
+
+#endif /* FEAT_GUI_W32 */
+
+
+#ifdef FEAT_MOUSE
+
+/*
+ * For the GUI the mouse handling is in gui_w32.c.
+ */
+# ifdef FEAT_GUI_W32
+    void
+mch_setmouse(
+    int on)
+{
+}
+# else
+static int g_fMouseAvail = FALSE;   /* mouse present */
+static int g_fMouseActive = FALSE;  /* mouse enabled */
+static int g_nMouseClick = -1;	    /* mouse status */
+static int g_xMouse;		    /* mouse x coordinate */
+static int g_yMouse;		    /* mouse y coordinate */
+
+/*
+ * Enable or disable mouse input
+ */
+    void
+mch_setmouse(
+    int on)
+{
+    DWORD cmodein;
+
+    if (!g_fMouseAvail)
+	return;
+
+    g_fMouseActive = on;
+    GetConsoleMode(g_hConIn, &cmodein);
+
+    if (g_fMouseActive)
+	cmodein |= ENABLE_MOUSE_INPUT;
+    else
+	cmodein &= ~ENABLE_MOUSE_INPUT;
+
+    SetConsoleMode(g_hConIn, cmodein);
+}
+
+
+/*
+ * Decode a MOUSE_EVENT.  If it's a valid event, return MOUSE_LEFT,
+ * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
+ * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
+ * or a MOUSE_LEFT, _MIDDLE, or _RIGHT.  We encode the button type,
+ * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
+ * and we return the mouse position in g_xMouse and g_yMouse.
+ *
+ * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
+ * MOUSE_DRAGs and one MOUSE_RELEASE.  MOUSE_RELEASE will be followed only
+ * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
+ *
+ * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
+ * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
+ *
+ * Windows will send us MOUSE_MOVED notifications whenever the mouse
+ * moves, even if it stays within the same character cell.  We ignore
+ * all MOUSE_MOVED messages if the position hasn't really changed, and
+ * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
+ * we're only interested in MOUSE_DRAG).
+ *
+ * All of this is complicated by the code that fakes MOUSE_MIDDLE on
+ * 2-button mouses by pressing the left & right buttons simultaneously.
+ * In practice, it's almost impossible to click both at the same time,
+ * so we need to delay a little.  Also, we tend not to get MOUSE_RELEASE
+ * in such cases, if the user is clicking quickly.
+ */
+    static BOOL
+decode_mouse_event(
+    MOUSE_EVENT_RECORD* pmer)
+{
+    static int s_nOldButton = -1;
+    static int s_nOldMouseClick = -1;
+    static int s_xOldMouse = -1;
+    static int s_yOldMouse = -1;
+    static linenr_T s_old_topline = 0;
+#ifdef FEAT_DIFF
+    static int s_old_topfill = 0;
+#endif
+    static int s_cClicks = 1;
+    static BOOL s_fReleased = TRUE;
+    static DWORD s_dwLastClickTime = 0;
+    static BOOL s_fNextIsMiddle = FALSE;
+
+    static DWORD cButtons = 0;	/* number of buttons supported */
+
+    const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
+    const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
+    const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
+    const DWORD LEFT_RIGHT = LEFT | RIGHT;
+
+    int nButton;
+
+    if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons))
+	cButtons = 2;
+
+    if (!g_fMouseAvail || !g_fMouseActive)
+    {
+	g_nMouseClick = -1;
+	return FALSE;
+    }
+
+    /* get a spurious MOUSE_EVENT immediately after receiving focus; ignore */
+    if (g_fJustGotFocus)
+    {
+	g_fJustGotFocus = FALSE;
+	return FALSE;
+    }
+
+    /* unprocessed mouse click? */
+    if (g_nMouseClick != -1)
+	return TRUE;
+
+    nButton = -1;
+    g_xMouse = pmer->dwMousePosition.X;
+    g_yMouse = pmer->dwMousePosition.Y;
+
+    if (pmer->dwEventFlags == MOUSE_MOVED)
+    {
+	/* ignore MOUSE_MOVED events if (x, y) hasn't changed.	(We get these
+	 * events even when the mouse moves only within a char cell.) */
+	if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse)
+	    return FALSE;
+    }
+
+    /* If no buttons are pressed... */
+    if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0)
+    {
+	/* If the last thing returned was MOUSE_RELEASE, ignore this */
+	if (s_fReleased)
+	    return FALSE;
+
+	nButton = MOUSE_RELEASE;
+	s_fReleased = TRUE;
+    }
+    else    /* one or more buttons pressed */
+    {
+	/* on a 2-button mouse, hold down left and right buttons
+	 * simultaneously to get MIDDLE. */
+
+	if (cButtons == 2 && s_nOldButton != MOUSE_DRAG)
+	{
+	    DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
+
+	    /* if either left or right button only is pressed, see if the
+	     * the next mouse event has both of them pressed */
+	    if (dwLR == LEFT || dwLR == RIGHT)
+	    {
+		for (;;)
+		{
+		    /* wait a short time for next input event */
+		    if (WaitForSingleObject(g_hConIn, p_mouset / 3)
+							     != WAIT_OBJECT_0)
+			break;
+		    else
+		    {
+			DWORD cRecords = 0;
+			INPUT_RECORD ir;
+			MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
+
+			PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
+
+			if (cRecords == 0 || ir.EventType != MOUSE_EVENT
+				|| !(pmer2->dwButtonState & LEFT_RIGHT))
+			    break;
+			else
+			{
+			    if (pmer2->dwEventFlags != MOUSE_MOVED)
+			    {
+				ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
+
+				return decode_mouse_event(pmer2);
+			    }
+			    else if (s_xOldMouse == pmer2->dwMousePosition.X &&
+				     s_yOldMouse == pmer2->dwMousePosition.Y)
+			    {
+				/* throw away spurious mouse move */
+				ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
+
+				/* are there any more mouse events in queue? */
+				PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
+
+				if (cRecords==0 || ir.EventType != MOUSE_EVENT)
+				    break;
+			    }
+			    else
+				break;
+			}
+		    }
+		}
+	    }
+	}
+
+	if (s_fNextIsMiddle)
+	{
+	    nButton = (pmer->dwEventFlags == MOUSE_MOVED)
+		? MOUSE_DRAG : MOUSE_MIDDLE;
+	    s_fNextIsMiddle = FALSE;
+	}
+	else if (cButtons == 2	&&
+	    ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
+	{
+	    nButton = MOUSE_MIDDLE;
+
+	    if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED)
+	    {
+		s_fNextIsMiddle = TRUE;
+		nButton = MOUSE_RELEASE;
+	    }
+	}
+	else if ((pmer->dwButtonState & LEFT) == LEFT)
+	    nButton = MOUSE_LEFT;
+	else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
+	    nButton = MOUSE_MIDDLE;
+	else if ((pmer->dwButtonState & RIGHT) == RIGHT)
+	    nButton = MOUSE_RIGHT;
+
+	if (! s_fReleased && ! s_fNextIsMiddle
+		&& nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG)
+	    return FALSE;
+
+	s_fReleased = s_fNextIsMiddle;
+    }
+
+    if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK)
+    {
+	/* button pressed or released, without mouse moving */
+	if (nButton != -1 && nButton != MOUSE_RELEASE)
+	{
+	    DWORD dwCurrentTime = GetTickCount();
+
+	    if (s_xOldMouse != g_xMouse
+		    || s_yOldMouse != g_yMouse
+		    || s_nOldButton != nButton
+		    || s_old_topline != curwin->w_topline
+#ifdef FEAT_DIFF
+		    || s_old_topfill != curwin->w_topfill
+#endif
+		    || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset)
+	    {
+		s_cClicks = 1;
+	    }
+	    else if (++s_cClicks > 4)
+	    {
+		s_cClicks = 1;
+	    }
+
+	    s_dwLastClickTime = dwCurrentTime;
+	}
+    }
+    else if (pmer->dwEventFlags == MOUSE_MOVED)
+    {
+	if (nButton != -1 && nButton != MOUSE_RELEASE)
+	    nButton = MOUSE_DRAG;
+
+	s_cClicks = 1;
+    }
+
+    if (nButton == -1)
+	return FALSE;
+
+    if (nButton != MOUSE_RELEASE)
+	s_nOldButton = nButton;
+
+    g_nMouseClick = nButton;
+
+    if (pmer->dwControlKeyState & SHIFT_PRESSED)
+	g_nMouseClick |= MOUSE_SHIFT;
+    if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
+	g_nMouseClick |= MOUSE_CTRL;
+    if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED  | LEFT_ALT_PRESSED))
+	g_nMouseClick |= MOUSE_ALT;
+
+    if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE)
+	SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
+
+    /* only pass on interesting (i.e., different) mouse events */
+    if (s_xOldMouse == g_xMouse
+	    && s_yOldMouse == g_yMouse
+	    && s_nOldMouseClick == g_nMouseClick)
+    {
+	g_nMouseClick = -1;
+	return FALSE;
+    }
+
+    s_xOldMouse = g_xMouse;
+    s_yOldMouse = g_yMouse;
+    s_old_topline = curwin->w_topline;
+#ifdef FEAT_DIFF
+    s_old_topfill = curwin->w_topfill;
+#endif
+    s_nOldMouseClick = g_nMouseClick;
+
+    return TRUE;
+}
+
+# endif /* FEAT_GUI_W32 */
+#endif /* FEAT_MOUSE */
+
+
+#ifdef MCH_CURSOR_SHAPE
+/*
+ * Set the shape of the cursor.
+ * 'thickness' can be from 1 (thin) to 99 (block)
+ */
+    static void
+mch_set_cursor_shape(int thickness)
+{
+    CONSOLE_CURSOR_INFO ConsoleCursorInfo;
+    ConsoleCursorInfo.dwSize = thickness;
+    ConsoleCursorInfo.bVisible = s_cursor_visible;
+
+    SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo);
+    if (s_cursor_visible)
+	SetConsoleCursorPosition(g_hConOut, g_coord);
+}
+
+    void
+mch_update_cursor(void)
+{
+    int		idx;
+    int		thickness;
+
+    /*
+     * How the cursor is drawn depends on the current mode.
+     */
+    idx = get_shape_idx(FALSE);
+
+    if (shape_table[idx].shape == SHAPE_BLOCK)
+	thickness = 99;	/* 100 doesn't work on W95 */
+    else
+	thickness = shape_table[idx].percentage;
+    mch_set_cursor_shape(thickness);
+}
+#endif
+
+#ifndef FEAT_GUI_W32	    /* this isn't used for the GUI */
+/*
+ * Handle FOCUS_EVENT.
+ */
+    static void
+handle_focus_event(INPUT_RECORD ir)
+{
+    g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
+    ui_focus_change((int)g_fJustGotFocus);
+}
+
+/*
+ * Wait until console input from keyboard or mouse is available,
+ * or the time is up.
+ * Return TRUE if something is available FALSE if not.
+ */
+    static int
+WaitForChar(long msec)
+{
+    DWORD	    dwNow = 0, dwEndTime = 0;
+    INPUT_RECORD    ir;
+    DWORD	    cRecords;
+    char_u	    ch, ch2;
+
+    if (msec > 0)
+	/* Wait until the specified time has elapsed. */
+	dwEndTime = GetTickCount() + msec;
+    else if (msec < 0)
+	/* Wait forever. */
+	dwEndTime = INFINITE;
+
+    /* We need to loop until the end of the time period, because
+     * we might get multiple unusable mouse events in that time.
+     */
+    for (;;)
+    {
+#ifdef FEAT_CLIENTSERVER
+	serverProcessPendingMessages();
+#endif
+	if (0
+#ifdef FEAT_MOUSE
+		|| g_nMouseClick != -1
+#endif
+#ifdef FEAT_CLIENTSERVER
+		|| input_available()
+#endif
+	   )
+	    return TRUE;
+
+	if (msec > 0)
+	{
+	    /* If the specified wait time has passed, return. */
+	    dwNow = GetTickCount();
+	    if (dwNow >= dwEndTime)
+		break;
+	}
+	if (msec != 0)
+	{
+#ifdef FEAT_CLIENTSERVER
+	    /* Wait for either an event on the console input or a message in
+	     * the client-server window. */
+	    if (MsgWaitForMultipleObjects(1, &g_hConIn, FALSE,
+			  dwEndTime - dwNow, QS_SENDMESSAGE) != WAIT_OBJECT_0)
+#else
+	    if (WaitForSingleObject(g_hConIn, dwEndTime - dwNow)
+							     != WAIT_OBJECT_0)
+#endif
+		    continue;
+	}
+
+	cRecords = 0;
+	PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
+
+#ifdef FEAT_MBYTE_IME
+	if (State & CMDLINE && msg_row == Rows - 1)
+	{
+	    CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+	    if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
+	    {
+		if (csbi.dwCursorPosition.Y != msg_row)
+		{
+		    /* The screen is now messed up, must redraw the
+		     * command line and later all the windows. */
+		    redraw_all_later(CLEAR);
+		    cmdline_row -= (msg_row - csbi.dwCursorPosition.Y);
+		    redrawcmd();
+		}
+	    }
+	}
+#endif
+
+	if (cRecords > 0)
+	{
+	    if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
+	    {
+#ifdef FEAT_MBYTE_IME
+		/* Windows IME sends two '\n's with only one 'ENTER'.  First:
+		 * wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */
+		if (ir.Event.KeyEvent.uChar.UnicodeChar == 0
+			&& ir.Event.KeyEvent.wVirtualKeyCode == 13)
+		{
+		    ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
+		    continue;
+		}
+#endif
+		if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
+								 NULL, FALSE))
+		    return TRUE;
+	    }
+
+	    ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
+
+	    if (ir.EventType == FOCUS_EVENT)
+		handle_focus_event(ir);
+	    else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
+		shell_resized();
+#ifdef FEAT_MOUSE
+	    else if (ir.EventType == MOUSE_EVENT
+		    && decode_mouse_event(&ir.Event.MouseEvent))
+		return TRUE;
+#endif
+	}
+	else if (msec == 0)
+	    break;
+    }
+
+#ifdef FEAT_CLIENTSERVER
+    /* Something might have been received while we were waiting. */
+    if (input_available())
+	return TRUE;
+#endif
+    return FALSE;
+}
+
+#ifndef FEAT_GUI_MSWIN
+/*
+ * return non-zero if a character is available
+ */
+    int
+mch_char_avail()
+{
+    return WaitForChar(0L);
+}
+#endif
+
+/*
+ * Create the console input.  Used when reading stdin doesn't work.
+ */
+    static void
+create_conin(void)
+{
+    g_hConIn =	CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
+			FILE_SHARE_READ|FILE_SHARE_WRITE,
+			(LPSECURITY_ATTRIBUTES) NULL,
+			OPEN_EXISTING, (DWORD)NULL, (HANDLE)NULL);
+    did_create_conin = TRUE;
+}
+
+/*
+ * Get a keystroke or a mouse event
+ */
+    static char_u
+tgetch(int *pmodifiers, char_u *pch2)
+{
+    char_u ch;
+
+    for (;;)
+    {
+	INPUT_RECORD ir;
+	DWORD cRecords = 0;
+
+#ifdef FEAT_CLIENTSERVER
+	(void)WaitForChar(-1L);
+	if (input_available())
+	    return 0;
+# ifdef FEAT_MOUSE
+	if (g_nMouseClick != -1)
+	    return 0;
+# endif
+#endif
+	if (ReadConsoleInput(g_hConIn, &ir, 1, &cRecords) == 0)
+	{
+	    if (did_create_conin)
+		read_error_exit();
+	    create_conin();
+	    continue;
+	}
+
+	if (ir.EventType == KEY_EVENT)
+	{
+	    if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2,
+							    pmodifiers, TRUE))
+		return ch;
+	}
+	else if (ir.EventType == FOCUS_EVENT)
+	    handle_focus_event(ir);
+	else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
+	    shell_resized();
+#ifdef FEAT_MOUSE
+	else if (ir.EventType == MOUSE_EVENT)
+	{
+	    if (decode_mouse_event(&ir.Event.MouseEvent))
+		return 0;
+	}
+#endif
+    }
+}
+#endif /* !FEAT_GUI_W32 */
+
+
+/*
+ * mch_inchar(): low-level input funcion.
+ * Get one or more characters from the keyboard or the mouse.
+ * If time == 0, do not wait for characters.
+ * If time == n, wait a short time for characters.
+ * If time == -1, wait forever for characters.
+ * Returns the number of characters read into buf.
+ */
+    int
+mch_inchar(
+    char_u	*buf,
+    int		maxlen,
+    long	time,
+    int		tb_change_cnt)
+{
+#ifndef FEAT_GUI_W32	    /* this isn't used for the GUI */
+
+    int		len;
+    int		c;
+#ifdef FEAT_AUTOCMD
+    static int	once_already = 0;
+#endif
+#define TYPEAHEADLEN 20
+    static char_u   typeahead[TYPEAHEADLEN];	/* previously typed bytes. */
+    static int	    typeaheadlen = 0;
+
+    /* First use any typeahead that was kept because "buf" was too small. */
+    if (typeaheadlen > 0)
+	goto theend;
+
+#ifdef FEAT_SNIFF
+    if (want_sniff_request)
+    {
+	if (sniff_request_waiting)
+	{
+	    /* return K_SNIFF */
+	    typeahead[typeaheadlen++] = CSI;
+	    typeahead[typeaheadlen++] = (char_u)KS_EXTRA;
+	    typeahead[typeaheadlen++] = (char_u)KE_SNIFF;
+	    sniff_request_waiting = 0;
+	    want_sniff_request = 0;
+	    goto theend;
+	}
+	else if (time < 0 || time > 250)
+	{
+	    /* don't wait too long, a request might be pending */
+	    time = 250;
+	}
+    }
+#endif
+
+    if (time >= 0)
+    {
+	if (!WaitForChar(time))     /* no character available */
+	{
+#ifdef FEAT_AUTOCMD
+	    once_already = 0;
+#endif
+	    return 0;
+	}
+    }
+    else    /* time == -1, wait forever */
+    {
+	mch_set_winsize_now();	/* Allow winsize changes from now on */
+
+#ifdef FEAT_AUTOCMD
+	/* If there is no character available within 2 seconds (default),
+	 * write the autoscript file to disk */
+	if (once_already == 2)
+	    updatescript(0);
+	else if (once_already == 1)
+	{
+	    setcursor();
+	    once_already = 2;
+	    return 0;
+	}
+	else
+#endif
+	    if (!WaitForChar(p_ut))
+	{
+#ifdef FEAT_AUTOCMD
+	    if (has_cursorhold() && get_real_state() == NORMAL_BUSY)
+	    {
+		apply_autocmds(EVENT_CURSORHOLD, NULL, NULL, FALSE, curbuf);
+		update_screen(VALID);
+		once_already = 1;
+		return 0;
+	    }
+#endif
+	    updatescript(0);
+	}
+    }
+
+    /*
+     * Try to read as many characters as there are, until the buffer is full.
+     */
+
+    /* we will get at least one key. Get more if they are available. */
+    g_fCBrkPressed = FALSE;
+
+#ifdef MCH_WRITE_DUMP
+    if (fdDump)
+	fputc('[', fdDump);
+#endif
+
+    /* Keep looping until there is something in the typeahead buffer and more
+     * to get and still room in the buffer (up to two bytes for a char and
+     * three bytes for a modifier). */
+    while ((typeaheadlen == 0 || WaitForChar(0L))
+					  && typeaheadlen + 5 <= TYPEAHEADLEN)
+    {
+	if (typebuf_changed(tb_change_cnt))
+	{
+	    /* "buf" may be invalid now if a client put something in the
+	     * typeahead buffer and "buf" is in the typeahead buffer. */
+	    typeaheadlen = 0;
+	    break;
+	}
+#ifdef FEAT_MOUSE
+	if (g_nMouseClick != -1)
+	{
+# ifdef MCH_WRITE_DUMP
+	    if (fdDump)
+		fprintf(fdDump, "{%02x @ %d, %d}",
+			g_nMouseClick, g_xMouse, g_yMouse);
+# endif
+	    typeahead[typeaheadlen++] = ESC + 128;
+	    typeahead[typeaheadlen++] = 'M';
+	    typeahead[typeaheadlen++] = g_nMouseClick;
+	    typeahead[typeaheadlen++] = g_xMouse + '!';
+	    typeahead[typeaheadlen++] = g_yMouse + '!';
+	    g_nMouseClick = -1;
+	}
+	else
+#endif
+	{
+	    char_u	ch2 = NUL;
+	    int		modifiers = 0;
+
+	    c = tgetch(&modifiers, &ch2);
+
+	    if (typebuf_changed(tb_change_cnt))
+	    {
+		/* "buf" may be invalid now if a client put something in the
+		 * typeahead buffer and "buf" is in the typeahead buffer. */
+		typeaheadlen = 0;
+		break;
+	    }
+
+	    if (c == Ctrl_C && ctrl_c_interrupts)
+	    {
+#if defined(FEAT_CLIENTSERVER)
+		trash_input_buf();
+#endif
+		got_int = TRUE;
+	    }
+
+#ifdef FEAT_MOUSE
+	    if (g_nMouseClick == -1)
+#endif
+	    {
+		int	n = 1;
+
+		/* A key may have one or two bytes. */
+		typeahead[typeaheadlen] = c;
+		if (ch2 != NUL)
+		{
+		    typeahead[typeaheadlen + 1] = ch2;
+		    ++n;
+		}
+#ifdef FEAT_MBYTE
+		/* Only convert normal characters, not special keys.  Need to
+		 * convert before applying ALT, otherwise mapping <M-x> breaks
+		 * when 'tenc' is set. */
+		if (input_conv.vc_type != CONV_NONE
+						&& (ch2 == NUL || c != K_NUL))
+		    n = convert_input(typeahead + typeaheadlen, n,
+						 TYPEAHEADLEN - typeaheadlen);
+#endif
+
+		/* Use the ALT key to set the 8th bit of the character
+		 * when it's one byte, the 8th bit isn't set yet and not
+		 * using a double-byte encoding (would become a lead
+		 * byte). */
+		if ((modifiers & MOD_MASK_ALT)
+			&& n == 1
+			&& (typeahead[typeaheadlen] & 0x80) == 0
+#ifdef FEAT_MBYTE
+			&& !enc_dbcs
+#endif
+		   )
+		{
+		    typeahead[typeaheadlen] |= 0x80;
+		    modifiers &= ~MOD_MASK_ALT;
+		}
+
+		if (modifiers != 0)
+		{
+		    /* Prepend modifiers to the character. */
+		    mch_memmove(typeahead + typeaheadlen + 3,
+						 typeahead + typeaheadlen, n);
+		    typeahead[typeaheadlen++] = K_SPECIAL;
+		    typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
+		    typeahead[typeaheadlen++] =  modifiers;
+		}
+
+		typeaheadlen += n;
+
+#ifdef MCH_WRITE_DUMP
+		if (fdDump)
+		    fputc(c, fdDump);
+#endif
+	    }
+	}
+    }
+
+#ifdef MCH_WRITE_DUMP
+    if (fdDump)
+    {
+	fputs("]\n", fdDump);
+	fflush(fdDump);
+    }
+#endif
+
+#ifdef FEAT_AUTOCMD
+    once_already = 0;
+#endif
+
+theend:
+    /* Move typeahead to "buf", as much as fits. */
+    len = 0;
+    while (len < maxlen && typeaheadlen > 0)
+    {
+	buf[len++] = typeahead[0];
+	mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
+    }
+    return len;
+
+#else /* FEAT_GUI_W32 */
+    return 0;
+#endif /* FEAT_GUI_W32 */
+}
+
+#ifndef __MINGW32__
+# include <shellapi.h>	/* required for FindExecutable() */
+#endif
+
+    static int
+executable_exists(char *name)
+{
+    char location[2 * _MAX_PATH + 2];
+    char widename[2 * _MAX_PATH];
+
+    /* There appears to be a bug in FindExecutableA() on Windows NT.
+     * Use FindExecutableW() instead... */
+    if (g_PlatformId == VER_PLATFORM_WIN32_NT)
+    {
+	MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1,
+						 (LPWSTR)widename, _MAX_PATH);
+	if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"",
+					    (LPWSTR)location) > (HINSTANCE)32)
+	    return TRUE;
+    }
+    else
+    {
+	if (FindExecutableA((LPCTSTR)name, (LPCTSTR)"",
+					    (LPTSTR)location) > (HINSTANCE)32)
+	    return TRUE;
+    }
+    return FALSE;
+}
+
+#ifdef FEAT_GUI_W32
+
+/*
+ * GUI version of mch_init().
+ */
+    void
+mch_init()
+{
+#ifndef __MINGW32__
+    extern int _fmode;
+#endif
+
+    /* Let critical errors result in a failure, not in a dialog box.  Required
+     * for the timestamp test to work on removed floppies. */
+    SetErrorMode(SEM_FAILCRITICALERRORS);
+
+    _fmode = O_BINARY;		/* we do our own CR-LF translation */
+
+    /* Specify window size.  Is there a place to get the default from? */
+    Rows = 25;
+    Columns = 80;
+
+    /* Look for 'vimrun' */
+    if (!gui_is_win32s())
+    {
+	char_u vimrun_location[_MAX_PATH + 4];
+
+	/* First try in same directory as gvim.exe */
+	STRCPY(vimrun_location, exe_name);
+	STRCPY(gettail(vimrun_location), "vimrun.exe");
+	if (mch_getperm(vimrun_location) >= 0)
+	{
+	    if (*skiptowhite(vimrun_location) != NUL)
+	    {
+		/* Enclose path with white space in double quotes. */
+		mch_memmove(vimrun_location + 1, vimrun_location,
+						 STRLEN(vimrun_location) + 1);
+		*vimrun_location = '"';
+		STRCPY(gettail(vimrun_location), "vimrun\" ");
+	    }
+	    else
+		STRCPY(gettail(vimrun_location), "vimrun ");
+
+	    vimrun_path = (char *)vim_strsave(vimrun_location);
+	    s_dont_use_vimrun = FALSE;
+	}
+	else if (executable_exists("vimrun.exe"))
+	    s_dont_use_vimrun = FALSE;
+
+	/* Don't give the warning for a missing vimrun.exe right now, but only
+	 * when vimrun was supposed to be used.  Don't bother people that do
+	 * not need vimrun.exe. */
+	if (s_dont_use_vimrun)
+	    need_vimrun_warning = TRUE;
+    }
+
+    /*
+     * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
+     * Otherwise the default "findstr /n" is used.
+     */
+    if (!executable_exists("findstr.exe"))
+	set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
+
+#ifdef FEAT_CLIPBOARD
+    clip_init(TRUE);
+
+    /*
+     * Vim's own clipboard format recognises whether the text is char, line, or
+     * rectangular block.  Only useful for copying between two Vims.
+     * "VimClipboard" was used for previous versions, using the first
+     * character to specify MCHAR, MLINE or MBLOCK.
+     */
+    clip_star.format = RegisterClipboardFormat("VimClipboard2");
+    clip_star.format_raw = RegisterClipboardFormat("VimRawBytes");
+#endif
+}
+
+
+#else /* FEAT_GUI_W32 */
+
+#define SRWIDTH(sr) ((sr).Right - (sr).Left + 1)
+#define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1)
+
+/*
+ * ClearConsoleBuffer()
+ * Description:
+ *  Clears the entire contents of the console screen buffer, using the
+ *  specified attribute.
+ * Returns:
+ *  TRUE on success
+ */
+    static BOOL
+ClearConsoleBuffer(WORD wAttribute)
+{
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    COORD coord;
+    DWORD NumCells, dummy;
+
+    if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi))
+	return FALSE;
+
+    NumCells = csbi.dwSize.X * csbi.dwSize.Y;
+    coord.X = 0;
+    coord.Y = 0;
+    if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells,
+	    coord, &dummy))
+    {
+	return FALSE;
+    }
+    if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells,
+	    coord, &dummy))
+    {
+	return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*
+ * FitConsoleWindow()
+ * Description:
+ *  Checks if the console window will fit within given buffer dimensions.
+ *  Also, if requested, will shrink the window to fit.
+ * Returns:
+ *  TRUE on success
+ */
+    static BOOL
+FitConsoleWindow(
+    COORD dwBufferSize,
+    BOOL WantAdjust)
+{
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    COORD dwWindowSize;
+    BOOL NeedAdjust = FALSE;
+
+    if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
+    {
+	/*
+	 * A buffer resize will fail if the current console window does
+	 * not lie completely within that buffer.  To avoid this, we might
+	 * have to move and possibly shrink the window.
+	 */
+	if (csbi.srWindow.Right >= dwBufferSize.X)
+	{
+	    dwWindowSize.X = SRWIDTH(csbi.srWindow);
+	    if (dwWindowSize.X > dwBufferSize.X)
+		dwWindowSize.X = dwBufferSize.X;
+	    csbi.srWindow.Right = dwBufferSize.X - 1;
+	    csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X;
+	    NeedAdjust = TRUE;
+	}
+	if (csbi.srWindow.Bottom >= dwBufferSize.Y)
+	{
+	    dwWindowSize.Y = SRHEIGHT(csbi.srWindow);
+	    if (dwWindowSize.Y > dwBufferSize.Y)
+		dwWindowSize.Y = dwBufferSize.Y;
+	    csbi.srWindow.Bottom = dwBufferSize.Y - 1;
+	    csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y;
+	    NeedAdjust = TRUE;
+	}
+	if (NeedAdjust && WantAdjust)
+	{
+	    if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow))
+		return FALSE;
+	}
+	return TRUE;
+    }
+
+    return FALSE;
+}
+
+typedef struct ConsoleBufferStruct
+{
+    BOOL IsValid;
+    CONSOLE_SCREEN_BUFFER_INFO Info;
+    PCHAR_INFO Buffer;
+    COORD BufferSize;
+} ConsoleBuffer;
+
+/*
+ * SaveConsoleBuffer()
+ * Description:
+ *  Saves important information about the console buffer, including the
+ *  actual buffer contents.  The saved information is suitable for later
+ *  restoration by RestoreConsoleBuffer().
+ * Returns:
+ *  TRUE if all information was saved; FALSE otherwise
+ *  If FALSE, still sets cb->IsValid if buffer characteristics were saved.
+ */
+    static BOOL
+SaveConsoleBuffer(
+    ConsoleBuffer *cb)
+{
+    DWORD NumCells;
+    COORD BufferCoord;
+    SMALL_RECT ReadRegion;
+    WORD Y, Y_incr;
+
+    if (cb == NULL)
+	return FALSE;
+
+    if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
+    {
+	cb->IsValid = FALSE;
+	return FALSE;
+    }
+    cb->IsValid = TRUE;
+
+    /*
+     * Allocate a buffer large enough to hold the entire console screen
+     * buffer.  If this ConsoleBuffer structure has already been initialized
+     * with a buffer of the correct size, then just use that one.
+     */
+    if (!cb->IsValid || cb->Buffer == NULL ||
+	    cb->BufferSize.X != cb->Info.dwSize.X ||
+	    cb->BufferSize.Y != cb->Info.dwSize.Y)
+    {
+	cb->BufferSize.X = cb->Info.dwSize.X;
+	cb->BufferSize.Y = cb->Info.dwSize.Y;
+	NumCells = cb->BufferSize.X * cb->BufferSize.Y;
+	if (cb->Buffer != NULL)
+	    vim_free(cb->Buffer);
+	cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
+	if (cb->Buffer == NULL)
+	    return FALSE;
+    }
+
+    /*
+     * We will now copy the console screen buffer into our buffer.
+     * ReadConsoleOutput() seems to be limited as far as how much you
+     * can read at a time.  Empirically, this number seems to be about
+     * 12000 cells (rows * columns).  Start at position (0, 0) and copy
+     * in chunks until it is all copied.  The chunks will all have the
+     * same horizontal characteristics, so initialize them now.  The
+     * height of each chunk will be (12000 / width).
+     */
+    BufferCoord.X = 0;
+    ReadRegion.Left = 0;
+    ReadRegion.Right = cb->Info.dwSize.X - 1;
+    Y_incr = 12000 / cb->Info.dwSize.X;
+    for (Y = 0; Y < cb->BufferSize.Y; Y += Y_incr)
+    {
+	/*
+	 * Read into position (0, Y) in our buffer.
+	 */
+	BufferCoord.Y = Y;
+	/*
+	 * Read the region whose top left corner is (0, Y) and whose bottom
+	 * right corner is (width - 1, Y + Y_incr - 1).  This should define
+	 * a region of size width by Y_incr.  Don't worry if this region is
+	 * too large for the remaining buffer; it will be cropped.
+	 */
+	ReadRegion.Top = Y;
+	ReadRegion.Bottom = Y + Y_incr - 1;
+	if (!ReadConsoleOutput(g_hConOut,	/* output handle */
+		cb->Buffer,			/* our buffer */
+		cb->BufferSize,			/* dimensions of our buffer */
+		BufferCoord,			/* offset in our buffer */
+		&ReadRegion))			/* region to save */
+	{
+	    vim_free(cb->Buffer);
+	    cb->Buffer = NULL;
+	    return FALSE;
+	}
+    }
+
+    return TRUE;
+}
+
+/*
+ * RestoreConsoleBuffer()
+ * Description:
+ *  Restores important information about the console buffer, including the
+ *  actual buffer contents, if desired.  The information to restore is in
+ *  the same format used by SaveConsoleBuffer().
+ * Returns:
+ *  TRUE on success
+ */
+    static BOOL
+RestoreConsoleBuffer(
+    ConsoleBuffer *cb,
+    BOOL RestoreScreen)
+{
+    COORD BufferCoord;
+    SMALL_RECT WriteRegion;
+
+    if (cb == NULL || !cb->IsValid)
+	return FALSE;
+
+    /*
+     * Before restoring the buffer contents, clear the current buffer, and
+     * restore the cursor position and window information.  Doing this now
+     * prevents old buffer contents from "flashing" onto the screen.
+     */
+    if (RestoreScreen)
+	ClearConsoleBuffer(cb->Info.wAttributes);
+
+    FitConsoleWindow(cb->Info.dwSize, TRUE);
+    if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
+	return FALSE;
+    if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
+	return FALSE;
+
+    if (!RestoreScreen)
+    {
+	/*
+	 * No need to restore the screen buffer contents, so we're done.
+	 */
+	return TRUE;
+    }
+
+    if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
+	return FALSE;
+    if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
+	return FALSE;
+
+    /*
+     * Restore the screen buffer contents.
+     */
+    if (cb->Buffer != NULL)
+    {
+	BufferCoord.X = 0;
+	BufferCoord.Y = 0;
+	WriteRegion.Left = 0;
+	WriteRegion.Top = 0;
+	WriteRegion.Right = cb->Info.dwSize.X - 1;
+	WriteRegion.Bottom = cb->Info.dwSize.Y - 1;
+	if (!WriteConsoleOutput(g_hConOut,	/* output handle */
+		cb->Buffer,			/* our buffer */
+		cb->BufferSize,			/* dimensions of our buffer */
+		BufferCoord,			/* offset in our buffer */
+		&WriteRegion))			/* region to restore */
+	{
+	    return FALSE;
+	}
+    }
+
+    return TRUE;
+}
+
+#ifdef FEAT_RESTORE_ORIG_SCREEN
+static ConsoleBuffer g_cbOrig = { 0 };
+#endif
+static ConsoleBuffer g_cbNonTermcap = { 0 };
+static ConsoleBuffer g_cbTermcap = { 0 };
+
+#ifdef FEAT_TITLE
+#ifdef __BORLANDC__
+typedef HWND (__stdcall *GETCONSOLEWINDOWPROC)(VOID);
+#else
+typedef WINBASEAPI HWND (WINAPI *GETCONSOLEWINDOWPROC)(VOID);
+#endif
+char g_szOrigTitle[256] = { 0 };
+HWND g_hWnd = NULL;	/* also used in os_mswin.c */
+static HICON g_hOrigIconSmall = NULL;
+static HICON g_hOrigIcon = NULL;
+static HICON g_hVimIcon = NULL;
+static BOOL g_fCanChangeIcon = FALSE;
+
+/* ICON* are not defined in VC++ 4.0 */
+#ifndef ICON_SMALL
+#define ICON_SMALL 0
+#endif
+#ifndef ICON_BIG
+#define ICON_BIG 1
+#endif
+/*
+ * GetConsoleIcon()
+ * Description:
+ *  Attempts to retrieve the small icon and/or the big icon currently in
+ *  use by a given window.
+ * Returns:
+ *  TRUE on success
+ */
+    static BOOL
+GetConsoleIcon(
+    HWND hWnd,
+    HICON *phIconSmall,
+    HICON *phIcon)
+{
+    if (hWnd == NULL)
+	return FALSE;
+
+    if (phIconSmall != NULL)
+    {
+	*phIconSmall = (HICON) SendMessage(hWnd, WM_GETICON,
+			    (WPARAM) ICON_SMALL, (LPARAM) 0);
+    }
+    if (phIcon != NULL)
+    {
+	*phIcon = (HICON) SendMessage(hWnd, WM_GETICON,
+			    (WPARAM) ICON_BIG, (LPARAM) 0);
+    }
+    return TRUE;
+}
+
+/*
+ * SetConsoleIcon()
+ * Description:
+ *  Attempts to change the small icon and/or the big icon currently in
+ *  use by a given window.
+ * Returns:
+ *  TRUE on success
+ */
+    static BOOL
+SetConsoleIcon(
+    HWND hWnd,
+    HICON hIconSmall,
+    HICON hIcon)
+{
+    HICON hPrevIconSmall;
+    HICON hPrevIcon;
+
+    if (hWnd == NULL)
+	return FALSE;
+
+    if (hIconSmall != NULL)
+    {
+	hPrevIconSmall = (HICON) SendMessage(hWnd, WM_SETICON,
+			    (WPARAM) ICON_SMALL, (LPARAM) hIconSmall);
+    }
+    if (hIcon != NULL)
+    {
+	hPrevIcon = (HICON) SendMessage(hWnd, WM_SETICON,
+			    (WPARAM) ICON_BIG, (LPARAM) hIcon);
+    }
+    return TRUE;
+}
+
+/*
+ * SaveConsoleTitleAndIcon()
+ * Description:
+ *  Saves the current console window title in g_szOrigTitle, for later
+ *  restoration.  Also, attempts to obtain a handle to the console window,
+ *  and use it to save the small and big icons currently in use by the
+ *  console window.  This is not always possible on some versions of Windows;
+ *  nor is it possible when running Vim remotely using Telnet (since the
+ *  console window the user sees is owned by a remote process).
+ */
+    static void
+SaveConsoleTitleAndIcon(void)
+{
+    GETCONSOLEWINDOWPROC GetConsoleWindowProc;
+
+    /* Save the original title. */
+    if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle)))
+	return;
+
+    /*
+     * Obtain a handle to the console window using GetConsoleWindow() from
+     * KERNEL32.DLL; we need to handle in order to change the window icon.
+     * This function only exists on NT-based Windows, starting with Windows
+     * 2000.  On older operating systems, we can't change the window icon
+     * anyway.
+     */
+    if ((GetConsoleWindowProc = (GETCONSOLEWINDOWPROC)
+	    GetProcAddress(GetModuleHandle("KERNEL32.DLL"),
+		    "GetConsoleWindow")) != NULL)
+    {
+	g_hWnd = (*GetConsoleWindowProc)();
+    }
+    if (g_hWnd == NULL)
+	return;
+
+    /* Save the original console window icon. */
+    GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon);
+    if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL)
+	return;
+
+    /* Extract the first icon contained in the Vim executable. */
+    g_hVimIcon = ExtractIcon(NULL, exe_name, 0);
+    if (g_hVimIcon != NULL)
+	g_fCanChangeIcon = TRUE;
+}
+#endif
+
+static int g_fWindInitCalled = FALSE;
+static int g_fTermcapMode = FALSE;
+static CONSOLE_CURSOR_INFO g_cci;
+static DWORD g_cmodein = 0;
+static DWORD g_cmodeout = 0;
+
+/*
+ * non-GUI version of mch_init().
+ */
+    void
+mch_init()
+{
+#ifndef FEAT_RESTORE_ORIG_SCREEN
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+#endif
+#ifndef __MINGW32__
+    extern int _fmode;
+#endif
+
+    /* Let critical errors result in a failure, not in a dialog box.  Required
+     * for the timestamp test to work on removed floppies. */
+    SetErrorMode(SEM_FAILCRITICALERRORS);
+
+    _fmode = O_BINARY;		/* we do our own CR-LF translation */
+    out_flush();
+
+    /* Obtain handles for the standard Console I/O devices */
+    if (read_cmd_fd == 0)
+	g_hConIn =  GetStdHandle(STD_INPUT_HANDLE);
+    else
+	create_conin();
+    g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
+
+#ifdef FEAT_RESTORE_ORIG_SCREEN
+    /* Save the initial console buffer for later restoration */
+    SaveConsoleBuffer(&g_cbOrig);
+    g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
+#else
+    /* Get current text attributes */
+    GetConsoleScreenBufferInfo(g_hConOut, &csbi);
+    g_attrCurrent = g_attrDefault = csbi.wAttributes;
+#endif
+    if (cterm_normal_fg_color == 0)
+	cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
+    if (cterm_normal_bg_color == 0)
+	cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
+
+    /* set termcap codes to current text attributes */
+    update_tcap(g_attrCurrent);
+
+    GetConsoleCursorInfo(g_hConOut, &g_cci);
+    GetConsoleMode(g_hConIn,  &g_cmodein);
+    GetConsoleMode(g_hConOut, &g_cmodeout);
+
+#ifdef FEAT_TITLE
+    SaveConsoleTitleAndIcon();
+    /*
+     * Set both the small and big icons of the console window to Vim's icon.
+     * Note that Vim presently only has one size of icon (32x32), but it
+     * automatically gets scaled down to 16x16 when setting the small icon.
+     */
+    if (g_fCanChangeIcon)
+	SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon);
+#endif
+
+    ui_get_shellsize();
+
+#ifdef MCH_WRITE_DUMP
+    fdDump = fopen("dump", "wt");
+
+    if (fdDump)
+    {
+	time_t t;
+
+	time(&t);
+	fputs(ctime(&t), fdDump);
+	fflush(fdDump);
+    }
+#endif
+
+    g_fWindInitCalled = TRUE;
+
+#ifdef FEAT_MOUSE
+    g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
+#endif
+
+#ifdef FEAT_CLIPBOARD
+    clip_init(TRUE);
+
+    /*
+     * Vim's own clipboard format recognises whether the text is char, line, or
+     * rectangular block.  Only useful for copying between two Vims.
+     * "VimClipboard" was used for previous versions, using the first
+     * character to specify MCHAR, MLINE or MBLOCK.
+     */
+    clip_star.format = RegisterClipboardFormat("VimClipboard2");
+    clip_star.format_raw = RegisterClipboardFormat("VimRawBytes");
+#endif
+
+    /* This will be NULL on anything but NT 4.0 */
+    s_pfnGetConsoleKeyboardLayoutName =
+	(PFNGCKLN) GetProcAddress(GetModuleHandle("kernel32.dll"),
+				  "GetConsoleKeyboardLayoutNameA");
+}
+
+/*
+ * non-GUI version of mch_exit().
+ * Shut down and exit with status `r'
+ * Careful: mch_exit() may be called before mch_init()!
+ */
+    void
+mch_exit(int r)
+{
+    stoptermcap();
+
+    if (g_fWindInitCalled)
+	settmode(TMODE_COOK);
+
+    ml_close_all(TRUE);		/* remove all memfiles */
+
+    if (g_fWindInitCalled)
+    {
+#ifdef FEAT_TITLE
+	mch_restore_title(3);
+	/*
+	 * Restore both the small and big icons of the console window to
+	 * what they were at startup.  Don't do this when the window is
+	 * closed, Vim would hang here.
+	 */
+	if (g_fCanChangeIcon && !g_fForceExit)
+	    SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon);
+#endif
+
+#ifdef MCH_WRITE_DUMP
+	if (fdDump)
+	{
+	    time_t t;
+
+	    time(&t);
+	    fputs(ctime(&t), fdDump);
+	    fclose(fdDump);
+	}
+	fdDump = NULL;
+#endif
+    }
+
+    SetConsoleCursorInfo(g_hConOut, &g_cci);
+    SetConsoleMode(g_hConIn,  g_cmodein);
+    SetConsoleMode(g_hConOut, g_cmodeout);
+
+#ifdef DYNAMIC_GETTEXT
+    dyn_libintl_end();
+#endif
+
+    exit(r);
+}
+#endif /* !FEAT_GUI_W32 */
+
+
+/*
+ * Do we have an interactive window?
+ */
+    int
+mch_check_win(
+    int argc,
+    char **argv)
+{
+    get_exe_name();
+
+#ifdef FEAT_GUI_W32
+    return OK;	    /* GUI always has a tty */
+#else
+    if (isatty(1))
+	return OK;
+    return FAIL;
+#endif
+}
+
+
+/*
+ * fname_case(): Set the case of the file name, if it already exists.
+ * When "len" is > 0, also expand short to long filenames.
+ */
+    void
+fname_case(
+    char_u	*name,
+    int		len)
+{
+    char		szTrueName[_MAX_PATH + 2];
+    char		*ptrue, *ptruePrev;
+    char		*porig, *porigPrev;
+    int			flen;
+    WIN32_FIND_DATA	fb;
+    HANDLE		hFind;
+    int			c;
+
+    flen = (name != NULL) ? (int)STRLEN(name) : 0;
+    if (flen == 0 || flen > _MAX_PATH)
+	return;
+
+    slash_adjust(name);
+
+    /* Build the new name in szTrueName[] one component at a time. */
+    porig = name;
+    ptrue = szTrueName;
+
+    if (isalpha(porig[0]) && porig[1] == ':')
+    {
+	/* copy leading drive letter */
+	*ptrue++ = *porig++;
+	*ptrue++ = *porig++;
+	*ptrue = NUL;	    /* in case nothing follows */
+    }
+
+    while (*porig != NUL)
+    {
+	/* copy \ characters */
+	while (*porig == psepc)
+	    *ptrue++ = *porig++;
+
+	ptruePrev = ptrue;
+	porigPrev = porig;
+	while (*porig != NUL && *porig != psepc)
+	{
+#ifdef FEAT_MBYTE
+	    int l;
+
+	    if (enc_dbcs)
+	    {
+		l = (*mb_ptr2len_check)(porig);
+		while (--l >= 0)
+		    *ptrue++ = *porig++;
+	    }
+	    else
+#endif
+		*ptrue++ = *porig++;
+	}
+	*ptrue = NUL;
+
+	/* Skip "", "." and "..". */
+	if (ptrue > ptruePrev
+		&& (ptruePrev[0] != '.'
+		    || (ptruePrev[1] != NUL
+			&& (ptruePrev[1] != '.' || ptruePrev[2] != NUL)))
+		&& (hFind = FindFirstFile(szTrueName, &fb))
+						      != INVALID_HANDLE_VALUE)
+	{
+	    c = *porig;
+	    *porig = NUL;
+
+	    /* Only use the match when it's the same name (ignoring case) or
+	     * expansion is allowed and there is a match with the short name
+	     * and there is enough room. */
+	    if (_stricoll(porigPrev, fb.cFileName) == 0
+		    || (len > 0
+			&& (_stricoll(porigPrev, fb.cAlternateFileName) == 0
+			    && (int)(ptruePrev - szTrueName)
+					   + (int)strlen(fb.cFileName) < len)))
+	    {
+		STRCPY(ptruePrev, fb.cFileName);
+
+		/* Look for exact match and prefer it if found.  Must be a
+		 * long name, otherwise there would be only one match. */
+		while (FindNextFile(hFind, &fb))
+		{
+		    if (*fb.cAlternateFileName != NUL
+			    && (strcoll(porigPrev, fb.cFileName) == 0
+				|| (len > 0
+				    && (_stricoll(porigPrev,
+						   fb.cAlternateFileName) == 0
+				    && (int)(ptruePrev - szTrueName)
+					 + (int)strlen(fb.cFileName) < len))))
+		    {
+			STRCPY(ptruePrev, fb.cFileName);
+			break;
+		    }
+		}
+	    }
+	    FindClose(hFind);
+	    *porig = c;
+	    ptrue = ptruePrev + strlen(ptruePrev);
+	}
+    }
+
+    STRCPY(name, szTrueName);
+}
+
+
+/*
+ * Insert user name in s[len].
+ */
+    int
+mch_get_user_name(
+    char_u *s,
+    int len)
+{
+    char szUserName[MAX_COMPUTERNAME_LENGTH + 1];
+    DWORD cch = sizeof szUserName;
+
+    if (GetUserName(szUserName, &cch))
+    {
+	STRNCPY(s, szUserName, len);
+	return OK;
+    }
+    s[0] = NUL;
+    return FAIL;
+}
+
+
+/*
+ * Insert host name in s[len].
+ */
+    void
+mch_get_host_name(
+    char_u	*s,
+    int		len)
+{
+    DWORD cch = len;
+
+    if (!GetComputerName(s, &cch))
+    {
+	STRNCPY(s, "PC (Win32 Vim)", len);
+	s[len - 1] = NUL;	/* make sure it's terminated */
+    }
+}
+
+
+/*
+ * return process ID
+ */
+    long
+mch_get_pid()
+{
+    return (long)GetCurrentProcessId();
+}
+
+
+/*
+ * Get name of current directory into buffer 'buf' of length 'len' bytes.
+ * Return OK for success, FAIL for failure.
+ */
+    int
+mch_dirname(
+    char_u	*buf,
+    int		len)
+{
+    /*
+     * Originally this was:
+     *    return (getcwd(buf, len) != NULL ? OK : FAIL);
+     * But the Win32s known bug list says that getcwd() doesn't work
+     * so use the Win32 system call instead. <Negri>
+     */
+#ifdef FEAT_MBYTE
+    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
+    {
+	WCHAR	wbuf[_MAX_PATH + 1];
+
+	if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0)
+	{
+	    char_u  *p = ucs2_to_enc(wbuf, NULL);
+
+	    if (p != NULL)
+	    {
+		STRNCPY(buf, p, len - 1);
+		buf[len - 1] = NUL;
+		vim_free(p);
+		return OK;
+	    }
+	}
+	/* Retry with non-wide function (for Windows 98). */
+    }
+#endif
+    return (GetCurrentDirectory(len, buf) != 0 ? OK : FAIL);
+}
+
+/*
+ * get file permissions for `name'
+ * -1 : error
+ * else FILE_ATTRIBUTE_* defined in winnt.h
+ */
+    long
+mch_getperm(
+    char_u *name)
+{
+#ifdef FEAT_MBYTE
+    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
+    {
+	WCHAR	*p = enc_to_ucs2(name, NULL);
+	long	n;
+
+	if (p != NULL)
+	{
+	    n = (long)GetFileAttributesW(p);
+	    vim_free(p);
+	    if (n >= 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+		return n;
+	    /* Retry with non-wide function (for Windows 98). */
+	}
+    }
+#endif
+    return (long)GetFileAttributes((char *)name);
+}
+
+
+/*
+ * set file permission for `name' to `perm'
+ */
+    int
+mch_setperm(
+    char_u *name,
+    long perm)
+{
+    perm |= FILE_ATTRIBUTE_ARCHIVE;	/* file has changed, set archive bit */
+#ifdef FEAT_MBYTE
+    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
+    {
+	WCHAR	*p = enc_to_ucs2(name, NULL);
+	long	n;
+
+	if (p != NULL)
+	{
+	    n = (long)SetFileAttributesW(p, perm);
+	    vim_free(p);
+	    if (n || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+		return n ? OK : FAIL;
+	    /* Retry with non-wide function (for Windows 98). */
+	}
+    }
+#endif
+    return SetFileAttributes((char *)name, perm) ? OK : FAIL;
+}
+
+/*
+ * Set hidden flag for "name".
+ */
+    void
+mch_hide(char_u *name)
+{
+    int		perm;
+#ifdef FEAT_MBYTE
+    WCHAR	*p = NULL;
+
+    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
+	p = enc_to_ucs2(name, NULL);
+#endif
+
+#ifdef FEAT_MBYTE
+    if (p != NULL)
+    {
+	perm = GetFileAttributesW(p);
+	if (perm < 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+	{
+	    /* Retry with non-wide function (for Windows 98). */
+	    vim_free(p);
+	    p = NULL;
+	}
+    }
+    if (p == NULL)
+#endif
+	perm = GetFileAttributes((char *)name);
+    if (perm >= 0)
+    {
+	perm |= FILE_ATTRIBUTE_HIDDEN;
+#ifdef FEAT_MBYTE
+	if (p != NULL)
+	{
+	    if (SetFileAttributesW(p, perm) == 0
+		    && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+	    {
+		/* Retry with non-wide function (for Windows 98). */
+		vim_free(p);
+		p = NULL;
+	    }
+	}
+	if (p == NULL)
+#endif
+	    SetFileAttributes((char *)name, perm);
+    }
+#ifdef FEAT_MBYTE
+    vim_free(p);
+#endif
+}
+
+/*
+ * return TRUE if "name" is a directory
+ * return FALSE if "name" is not a directory or upon error
+ */
+    int
+mch_isdir(char_u *name)
+{
+    int f = mch_getperm(name);
+
+    if (f == -1)
+	return FALSE;		    /* file does not exist at all */
+
+    return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
+}
+
+/*
+ * Return TRUE if file or directory "name" is writable (not readonly).
+ * Strange semantics of Win32: a readonly directory is writable, but you can't
+ * delete a file.  Let's say this means it is writable.
+ */
+    int
+mch_writable(char_u *name)
+{
+    int perm = mch_getperm(name);
+
+    return (perm != -1 && (!(perm & FILE_ATTRIBUTE_READONLY)
+				       || (perm & FILE_ATTRIBUTE_DIRECTORY)));
+}
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Return 1 if "name" can be executed, 0 if not.
+ * Return -1 if unknown.
+ */
+    int
+mch_can_exe(char_u *name)
+{
+    return executable_exists((char *)name);
+}
+#endif
+
+/*
+ * Check what "name" is:
+ * NODE_NORMAL: file or directory (or doesn't exist)
+ * NODE_WRITABLE: writable device, socket, fifo, etc.
+ * NODE_OTHER: non-writable things
+ */
+    int
+mch_nodetype(char_u *name)
+{
+    HANDLE	hFile;
+    int		type;
+
+    hFile = CreateFile(name,		/* file name */
+		GENERIC_WRITE,		/* access mode */
+		0,			/* share mode */
+		NULL,			/* security descriptor */
+		OPEN_EXISTING,		/* creation disposition */
+		0,			/* file attributes */
+		NULL);			/* handle to template file */
+
+    if (hFile == INVALID_HANDLE_VALUE)
+	return NODE_NORMAL;
+
+    type = GetFileType(hFile);
+    CloseHandle(hFile);
+    if (type == FILE_TYPE_CHAR)
+	return NODE_WRITABLE;
+    if (type == FILE_TYPE_DISK)
+	return NODE_NORMAL;
+    return NODE_OTHER;
+}
+
+#ifdef HAVE_ACL
+struct my_acl
+{
+    PSECURITY_DESCRIPTOR    pSecurityDescriptor;
+    PSID		    pSidOwner;
+    PSID		    pSidGroup;
+    PACL		    pDacl;
+    PACL		    pSacl;
+};
+#endif
+
+/*
+ * Return a pointer to the ACL of file "fname" in allocated memory.
+ * Return NULL if the ACL is not available for whatever reason.
+ */
+    vim_acl_T
+mch_get_acl(fname)
+    char_u	*fname;
+{
+#ifndef HAVE_ACL
+    return (vim_acl_T)NULL;
+#else
+    struct my_acl   *p = NULL;
+
+    /* This only works on Windows NT and 2000. */
+    if (g_PlatformId == VER_PLATFORM_WIN32_NT && advapi_lib != NULL)
+    {
+	p = (struct my_acl *)alloc_clear((unsigned)sizeof(struct my_acl));
+	if (p != NULL)
+	{
+	    if (pGetNamedSecurityInfo(
+			(LPTSTR)fname,		// Abstract filename
+			SE_FILE_OBJECT,		// File Object
+			// Retrieve the entire security descriptor.
+			OWNER_SECURITY_INFORMATION |
+			GROUP_SECURITY_INFORMATION |
+			DACL_SECURITY_INFORMATION |
+			SACL_SECURITY_INFORMATION,
+			&p->pSidOwner,		// Ownership information.
+			&p->pSidGroup,		// Group membership.
+			&p->pDacl,		// Discretionary information.
+			&p->pSacl,		// For auditing purposes.
+			&p->pSecurityDescriptor
+				    ) != ERROR_SUCCESS)
+	    {
+		mch_free_acl((vim_acl_T)p);
+		p = NULL;
+	    }
+	}
+    }
+
+    return (vim_acl_T)p;
+#endif
+}
+
+/*
+ * Set the ACL of file "fname" to "acl" (unless it's NULL).
+ * Errors are ignored.
+ * This must only be called with "acl" equal to what mch_get_acl() returned.
+ */
+    void
+mch_set_acl(fname, acl)
+    char_u	*fname;
+    vim_acl_T	acl;
+{
+#ifdef HAVE_ACL
+    struct my_acl   *p = (struct my_acl *)acl;
+
+    if (p != NULL && advapi_lib != NULL)
+	(void)pSetNamedSecurityInfo(
+		    (LPTSTR)fname,		// Abstract filename
+		    SE_FILE_OBJECT,		// File Object
+		    // Retrieve the entire security descriptor.
+		    OWNER_SECURITY_INFORMATION |
+			GROUP_SECURITY_INFORMATION |
+			DACL_SECURITY_INFORMATION |
+			SACL_SECURITY_INFORMATION,
+		    p->pSidOwner,		// Ownership information.
+		    p->pSidGroup,		// Group membership.
+		    p->pDacl,			// Discretionary information.
+		    p->pSacl			// For auditing purposes.
+		    );
+#endif
+}
+
+    void
+mch_free_acl(acl)
+    vim_acl_T	acl;
+{
+#ifdef HAVE_ACL
+    struct my_acl   *p = (struct my_acl *)acl;
+
+    if (p != NULL)
+    {
+	LocalFree(p->pSecurityDescriptor);	// Free the memory just in case
+	vim_free(p);
+    }
+#endif
+}
+
+#ifndef FEAT_GUI_W32
+
+/*
+ * handler for ctrl-break, ctrl-c interrupts, and fatal events.
+ */
+    static BOOL WINAPI
+handler_routine(
+    DWORD dwCtrlType)
+{
+    switch (dwCtrlType)
+    {
+    case CTRL_C_EVENT:
+	if (ctrl_c_interrupts)
+	    g_fCtrlCPressed = TRUE;
+	return TRUE;
+
+    case CTRL_BREAK_EVENT:
+	g_fCBrkPressed	= TRUE;
+	return TRUE;
+
+    /* fatal events: shut down gracefully */
+    case CTRL_CLOSE_EVENT:
+    case CTRL_LOGOFF_EVENT:
+    case CTRL_SHUTDOWN_EVENT:
+	windgoto((int)Rows - 1, 0);
+	g_fForceExit = TRUE;
+
+	sprintf((char *)IObuff, _("Vim: Caught %s event\n"),
+		(dwCtrlType == CTRL_CLOSE_EVENT
+		     ? _("close")
+		     : dwCtrlType == CTRL_LOGOFF_EVENT
+			 ? _("logoff")
+			 : _("shutdown")));
+#ifdef DEBUG
+	OutputDebugString(IObuff);
+#endif
+
+	preserve_exit();	/* output IObuff, preserve files and exit */
+
+	return TRUE;		/* not reached */
+
+    default:
+	return FALSE;
+    }
+}
+
+
+/*
+ * set the tty in (raw) ? "raw" : "cooked" mode
+ */
+    void
+mch_settmode(
+    int tmode)
+{
+    DWORD cmodein;
+    DWORD cmodeout;
+    BOOL bEnableHandler;
+
+    GetConsoleMode(g_hConIn, &cmodein);
+    GetConsoleMode(g_hConOut, &cmodeout);
+    if (tmode == TMODE_RAW)
+    {
+	cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
+		     ENABLE_ECHO_INPUT);
+#ifdef FEAT_MOUSE
+	if (g_fMouseActive)
+	    cmodein |= ENABLE_MOUSE_INPUT;
+#endif
+	cmodeout &= ~(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
+	bEnableHandler = TRUE;
+    }
+    else /* cooked */
+    {
+	cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
+		    ENABLE_ECHO_INPUT);
+	cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
+	bEnableHandler = FALSE;
+    }
+    SetConsoleMode(g_hConIn, cmodein);
+    SetConsoleMode(g_hConOut, cmodeout);
+    SetConsoleCtrlHandler(handler_routine, bEnableHandler);
+
+#ifdef MCH_WRITE_DUMP
+    if (fdDump)
+    {
+	fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n",
+		tmode == TMODE_RAW ? "raw" :
+				    tmode == TMODE_COOK ? "cooked" : "normal",
+		cmodein, cmodeout);
+	fflush(fdDump);
+    }
+#endif
+}
+
+
+/*
+ * Get the size of the current window in `Rows' and `Columns'
+ * Return OK when size could be determined, FAIL otherwise.
+ */
+    int
+mch_get_shellsize()
+{
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+    if (!g_fTermcapMode && g_cbTermcap.IsValid)
+    {
+	/*
+	 * For some reason, we are trying to get the screen dimensions
+	 * even though we are not in termcap mode.  The 'Rows' and 'Columns'
+	 * variables are really intended to mean the size of Vim screen
+	 * while in termcap mode.
+	 */
+	Rows = g_cbTermcap.Info.dwSize.Y;
+	Columns = g_cbTermcap.Info.dwSize.X;
+    }
+    else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
+    {
+	Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
+	Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
+    }
+    else
+    {
+	Rows = 25;
+	Columns = 80;
+    }
+    return OK;
+}
+
+/*
+ * Set a console window to `xSize' * `ySize'
+ */
+    static void
+ResizeConBufAndWindow(
+    HANDLE hConsole,
+    int xSize,
+    int ySize)
+{
+    CONSOLE_SCREEN_BUFFER_INFO csbi;	/* hold current console buffer info */
+    SMALL_RECT	    srWindowRect;	/* hold the new console size */
+    COORD	    coordScreen;
+
+#ifdef MCH_WRITE_DUMP
+    if (fdDump)
+    {
+	fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
+	fflush(fdDump);
+    }
+#endif
+
+    /* get the largest size we can size the console window to */
+    coordScreen = GetLargestConsoleWindowSize(hConsole);
+
+    /* define the new console window size and scroll position */
+    srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
+    srWindowRect.Right =  (SHORT) (min(xSize, coordScreen.X) - 1);
+    srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
+
+    if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
+    {
+	int sx, sy;
+
+	sx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
+	sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
+	if (sy < ySize || sx < xSize)
+	{
+	    /*
+	     * Increasing number of lines/columns, do buffer first.
+	     * Use the maximal size in x and y direction.
+	     */
+	    if (sy < ySize)
+		coordScreen.Y = ySize;
+	    else
+		coordScreen.Y = sy;
+	    if (sx < xSize)
+		coordScreen.X = xSize;
+	    else
+		coordScreen.X = sx;
+	    SetConsoleScreenBufferSize(hConsole, coordScreen);
+	}
+    }
+
+    if (!SetConsoleWindowInfo(g_hConOut, TRUE, &srWindowRect))
+    {
+#ifdef MCH_WRITE_DUMP
+	if (fdDump)
+	{
+	    fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
+		    GetLastError());
+	    fflush(fdDump);
+	}
+#endif
+    }
+
+    /* define the new console buffer size */
+    coordScreen.X = xSize;
+    coordScreen.Y = ySize;
+
+    if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
+    {
+#ifdef MCH_WRITE_DUMP
+	if (fdDump)
+	{
+	    fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
+		    GetLastError());
+	    fflush(fdDump);
+	}
+#endif
+    }
+}
+
+
+/*
+ * Set the console window to `Rows' * `Columns'
+ */
+    void
+mch_set_shellsize()
+{
+    COORD coordScreen;
+
+    /* Don't change window size while still starting up */
+    if (suppress_winsize != 0)
+    {
+	suppress_winsize = 2;
+	return;
+    }
+
+    if (term_console)
+    {
+	coordScreen = GetLargestConsoleWindowSize(g_hConOut);
+
+	/* Clamp Rows and Columns to reasonable values */
+	if (Rows > coordScreen.Y)
+	    Rows = coordScreen.Y;
+	if (Columns > coordScreen.X)
+	    Columns = coordScreen.X;
+
+	ResizeConBufAndWindow(g_hConOut, Columns, Rows);
+    }
+}
+
+/*
+ * Rows and/or Columns has changed.
+ */
+    void
+mch_new_shellsize()
+{
+    set_scroll_region(0, 0, Columns - 1, Rows - 1);
+}
+
+
+/*
+ * Called when started up, to set the winsize that was delayed.
+ */
+    void
+mch_set_winsize_now()
+{
+    if (suppress_winsize == 2)
+    {
+	suppress_winsize = 0;
+	mch_set_shellsize();
+	shell_resized();
+    }
+    suppress_winsize = 0;
+}
+#endif /* FEAT_GUI_W32 */
+
+
+
+#if defined(FEAT_GUI_W32) || defined(PROTO)
+
+/*
+ * Specialised version of system() for Win32 GUI mode.
+ * This version proceeds as follows:
+ *    1. Create a console window for use by the subprocess
+ *    2. Run the subprocess (it gets the allocated console by default)
+ *    3. Wait for the subprocess to terminate and get its exit code
+ *    4. Prompt the user to press a key to close the console window
+ */
+    static int
+mch_system(char *cmd, int options)
+{
+    STARTUPINFO		si;
+    PROCESS_INFORMATION pi;
+    DWORD		ret = 0;
+    HWND		hwnd = GetFocus();
+
+    si.cb = sizeof(si);
+    si.lpReserved = NULL;
+    si.lpDesktop = NULL;
+    si.lpTitle = NULL;
+    si.dwFlags = STARTF_USESHOWWINDOW;
+    /*
+     * It's nicer to run a filter command in a minimized window, but in
+     * Windows 95 this makes the command MUCH slower.  We can't do it under
+     * Win32s either as it stops the synchronous spawn workaround working.
+     */
+    if ((options & SHELL_DOOUT) && !mch_windows95() && !gui_is_win32s())
+	si.wShowWindow = SW_SHOWMINIMIZED;
+    else
+	si.wShowWindow = SW_SHOWNORMAL;
+    si.cbReserved2 = 0;
+    si.lpReserved2 = NULL;
+
+    /* There is a strange error on Windows 95 when using "c:\\command.com".
+     * When the "c:\\" is left out it works OK...? */
+    if (mch_windows95()
+	    && (STRNICMP(cmd, "c:/command.com", 14) == 0
+		|| STRNICMP(cmd, "c:\\command.com", 14) == 0))
+	cmd += 3;
+
+    /* Now, run the command */
+    CreateProcess(NULL,			/* Executable name */
+		  cmd,			/* Command to execute */
+		  NULL,			/* Process security attributes */
+		  NULL,			/* Thread security attributes */
+		  FALSE,		/* Inherit handles */
+		  CREATE_DEFAULT_ERROR_MODE |	/* Creation flags */
+			CREATE_NEW_CONSOLE,
+		  NULL,			/* Environment */
+		  NULL,			/* Current directory */
+		  &si,			/* Startup information */
+		  &pi);			/* Process information */
+
+
+    /* Wait for the command to terminate before continuing */
+    if (g_PlatformId != VER_PLATFORM_WIN32s)
+    {
+#ifdef FEAT_GUI
+	int	    delay = 1;
+
+	/* Keep updating the window while waiting for the shell to finish. */
+	for (;;)
+	{
+	    MSG	msg;
+
+	    if (PeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
+	    {
+		TranslateMessage(&msg);
+		DispatchMessage(&msg);
+	    }
+	    if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
+		break;
+
+	    /* We start waiting for a very short time and then increase it, so
+	     * that we respond quickly when the process is quick, and don't
+	     * consume too much overhead when it's slow. */
+	    if (delay < 50)
+		delay += 10;
+	}
+#else
+	WaitForSingleObject(pi.hProcess, INFINITE);
+#endif
+
+	/* Get the command exit code */
+	GetExitCodeProcess(pi.hProcess, &ret);
+    }
+    else
+    {
+	/*
+	 * This ugly code is the only quick way of performing
+	 * a synchronous spawn under Win32s. Yuk.
+	 */
+	num_windows = 0;
+	EnumWindows(win32ssynch_cb, 0);
+	old_num_windows = num_windows;
+	do
+	{
+	    Sleep(1000);
+	    num_windows = 0;
+	    EnumWindows(win32ssynch_cb, 0);
+	} while (num_windows == old_num_windows);
+	ret = 0;
+    }
+
+    /* Close the handles to the subprocess, so that it goes away */
+    CloseHandle(pi.hThread);
+    CloseHandle(pi.hProcess);
+
+    /* Try to get input focus back.  Doesn't always work though. */
+    PostMessage(hwnd, WM_SETFOCUS, 0, 0);
+
+    return ret;
+}
+#else
+
+# define mch_system(c, o) system(c)
+
+#endif
+
+/*
+ * Either execute a command by calling the shell or start a new shell
+ */
+    int
+mch_call_shell(
+    char_u *cmd,
+    int options)	/* SHELL_*, see vim.h */
+{
+    int		x = 0;
+    int		tmode = cur_tmode;
+#ifdef FEAT_TITLE
+    char szShellTitle[512];
+
+    /* Change the title to reflect that we are in a subshell. */
+    if (GetConsoleTitle(szShellTitle, sizeof(szShellTitle) - 4) > 0)
+    {
+	if (cmd == NULL)
+	    strcat(szShellTitle, " :sh");
+	else
+	{
+	    strcat(szShellTitle, " - !");
+	    if ((strlen(szShellTitle) + strlen(cmd) < sizeof(szShellTitle)))
+		strcat(szShellTitle, cmd);
+	}
+	mch_settitle(szShellTitle, NULL);
+    }
+#endif
+
+    out_flush();
+
+#ifdef MCH_WRITE_DUMP
+    if (fdDump)
+    {
+	fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
+	fflush(fdDump);
+    }
+#endif
+
+    /*
+     * Catch all deadly signals while running the external command, because a
+     * CTRL-C, Ctrl-Break or illegal instruction  might otherwise kill us.
+     */
+    signal(SIGINT, SIG_IGN);
+#if defined(__GNUC__) && !defined(__MINGW32__)
+    signal(SIGKILL, SIG_IGN);
+#else
+    signal(SIGBREAK, SIG_IGN);
+#endif
+    signal(SIGILL, SIG_IGN);
+    signal(SIGFPE, SIG_IGN);
+    signal(SIGSEGV, SIG_IGN);
+    signal(SIGTERM, SIG_IGN);
+    signal(SIGABRT, SIG_IGN);
+
+    if (options & SHELL_COOKED)
+	settmode(TMODE_COOK);	/* set to normal mode */
+
+    if (cmd == NULL)
+    {
+	x = mch_system(p_sh, options);
+    }
+    else
+    {
+	/* we use "command" or "cmd" to start the shell; slow but easy */
+	char_u *newcmd;
+
+	newcmd = lalloc((long_u) (
+#ifdef FEAT_GUI_W32
+		STRLEN(vimrun_path) +
+#endif
+		STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10), TRUE);
+	if (newcmd != NULL)
+	{
+	    char_u *cmdbase = (*cmd == '"' ? cmd + 1 : cmd);
+
+	    if ((STRNICMP(cmdbase, "start", 5) == 0) && vim_iswhite(cmdbase[5]))
+	    {
+		STARTUPINFO		si;
+		PROCESS_INFORMATION	pi;
+
+		si.cb = sizeof(si);
+		si.lpReserved = NULL;
+		si.lpDesktop = NULL;
+		si.lpTitle = NULL;
+		si.dwFlags = 0;
+		si.cbReserved2 = 0;
+		si.lpReserved2 = NULL;
+
+		cmdbase = skipwhite(cmdbase + 5);
+		if ((STRNICMP(cmdbase, "/min", 4) == 0)
+			&& vim_iswhite(cmdbase[4]))
+		{
+		    cmdbase = skipwhite(cmdbase + 4);
+		    si.dwFlags = STARTF_USESHOWWINDOW;
+		    si.wShowWindow = SW_SHOWMINNOACTIVE;
+		}
+
+		/* When the command is in double quotes, but 'shellxquote' is
+		 * empty, keep the double quotes around the command.
+		 * Otherwise remove the double quotes, they aren't needed
+		 * here, because we don't use a shell to run the command. */
+		if (*cmd == '"' && *p_sxq == NUL)
+		{
+		    newcmd[0] = '"';
+		    STRCPY(newcmd + 1, cmdbase);
+		}
+		else
+		{
+		    STRCPY(newcmd, cmdbase);
+		    if (*cmd == '"' && *newcmd != NUL)
+			newcmd[STRLEN(newcmd) - 1] = NUL;
+		}
+
+		/*
+		 * Now, start the command as a process, so that it doesn't
+		 * inherit our handles which causes unpleasant dangling swap
+		 * files if we exit before the spawned process
+		 */
+		if (CreateProcess (NULL,	// Executable name
+			newcmd,			// Command to execute
+			NULL,			// Process security attributes
+			NULL,			// Thread security attributes
+			FALSE,			// Inherit handles
+			CREATE_NEW_CONSOLE,	// Creation flags
+			NULL,			// Environment
+			NULL,			// Current directory
+			&si,			// Startup information
+			&pi))			// Process information
+		    x = 0;
+		else
+		{
+		    x = -1;
+#ifdef FEAT_GUI_W32
+		    EMSG(_("E371: Command not found"));
+#endif
+		}
+		/* Close the handles to the subprocess, so that it goes away */
+		CloseHandle(pi.hThread);
+		CloseHandle(pi.hProcess);
+	    }
+	    else
+	    {
+#if defined(FEAT_GUI_W32)
+		if (need_vimrun_warning)
+		{
+		    MessageBox(NULL,
+			    _("VIMRUN.EXE not found in your $PATH.\n"
+				"External commands will not pause after completion.\n"
+				"See  :help win32-vimrun  for more information."),
+			    _("Vim Warning"),
+			    MB_ICONWARNING);
+		    need_vimrun_warning = FALSE;
+		}
+		if (!s_dont_use_vimrun)
+		    /* Use vimrun to execute the command.  It opens a console
+		     * window, which can be closed without killing Vim. */
+		    sprintf((char *)newcmd, "%s%s%s %s %s",
+			    vimrun_path,
+			    (msg_silent != 0 || (options & SHELL_DOOUT))
+								 ? "-s " : "",
+			    p_sh, p_shcf, cmd);
+		else
+#endif
+		    sprintf((char *)newcmd, "%s %s %s", p_sh, p_shcf, cmd);
+		x = mch_system((char *)newcmd, options);
+	    }
+	    vim_free(newcmd);
+	}
+    }
+
+    if (tmode == TMODE_RAW)
+	settmode(TMODE_RAW);	/* set to raw mode */
+
+    /* Print the return value, unless "vimrun" was used. */
+    if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent
+#if defined(FEAT_GUI_W32)
+		&& ((options & SHELL_DOOUT) || s_dont_use_vimrun)
+#endif
+	    )
+    {
+	smsg(_("shell returned %d"), x);
+	msg_putchar('\n');
+    }
+#ifdef FEAT_TITLE
+    resettitle();
+#endif
+
+    signal(SIGINT, SIG_DFL);
+#if defined(__GNUC__) && !defined(__MINGW32__)
+    signal(SIGKILL, SIG_DFL);
+#else
+    signal(SIGBREAK, SIG_DFL);
+#endif
+    signal(SIGILL, SIG_DFL);
+    signal(SIGFPE, SIG_DFL);
+    signal(SIGSEGV, SIG_DFL);
+    signal(SIGTERM, SIG_DFL);
+    signal(SIGABRT, SIG_DFL);
+
+    return x;
+}
+
+
+#ifndef FEAT_GUI_W32
+
+/*
+ * Start termcap mode
+ */
+    static void
+termcap_mode_start(void)
+{
+    DWORD cmodein;
+
+    if (g_fTermcapMode)
+	return;
+
+    SaveConsoleBuffer(&g_cbNonTermcap);
+
+    if (g_cbTermcap.IsValid)
+    {
+	/*
+	 * We've been in termcap mode before.  Restore certain screen
+	 * characteristics, including the buffer size and the window
+	 * size.  Since we will be redrawing the screen, we don't need
+	 * to restore the actual contents of the buffer.
+	 */
+	RestoreConsoleBuffer(&g_cbTermcap, FALSE);
+	SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow);
+	Rows = g_cbTermcap.Info.dwSize.Y;
+	Columns = g_cbTermcap.Info.dwSize.X;
+    }
+    else
+    {
+	/*
+	 * This is our first time entering termcap mode.  Clear the console
+	 * screen buffer, and resize the buffer to match the current window
+	 * size.  We will use this as the size of our editing environment.
+	 */
+	ClearConsoleBuffer(g_attrCurrent);
+	ResizeConBufAndWindow(g_hConOut, Columns, Rows);
+    }
+
+#ifdef FEAT_TITLE
+    resettitle();
+#endif
+
+    GetConsoleMode(g_hConIn, &cmodein);
+#ifdef FEAT_MOUSE
+    if (g_fMouseActive)
+	cmodein |= ENABLE_MOUSE_INPUT;
+    else
+	cmodein &= ~ENABLE_MOUSE_INPUT;
+#endif
+    cmodein |= ENABLE_WINDOW_INPUT;
+    SetConsoleMode(g_hConIn, cmodein);
+
+    redraw_later_clear();
+    g_fTermcapMode = TRUE;
+}
+
+
+/*
+ * End termcap mode
+ */
+    static void
+termcap_mode_end(void)
+{
+    DWORD cmodein;
+    ConsoleBuffer *cb;
+    COORD coord;
+    DWORD dwDummy;
+
+    if (!g_fTermcapMode)
+	return;
+
+    SaveConsoleBuffer(&g_cbTermcap);
+
+    GetConsoleMode(g_hConIn, &cmodein);
+    cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
+    SetConsoleMode(g_hConIn, cmodein);
+
+#ifdef FEAT_RESTORE_ORIG_SCREEN
+    cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
+#else
+    cb = &g_cbNonTermcap;
+#endif
+    RestoreConsoleBuffer(cb, p_rs);
+    SetConsoleCursorInfo(g_hConOut, &g_cci);
+
+    if (p_rs || exiting)
+    {
+	/*
+	 * Clear anything that happens to be on the current line.
+	 */
+	coord.X = 0;
+	coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1));
+	FillConsoleOutputCharacter(g_hConOut, ' ',
+		cb->Info.dwSize.X, coord, &dwDummy);
+	/*
+	 * The following is just for aesthetics.  If we are exiting without
+	 * restoring the screen, then we want to have a prompt string
+	 * appear at the bottom line.  However, the command interpreter
+	 * seems to always advance the cursor one line before displaying
+	 * the prompt string, which causes the screen to scroll.  To
+	 * counter this, move the cursor up one line before exiting.
+	 */
+	if (exiting && !p_rs)
+	    coord.Y--;
+	/*
+	 * Position the cursor at the leftmost column of the desired row.
+	 */
+	SetConsoleCursorPosition(g_hConOut, coord);
+    }
+
+    g_fTermcapMode = FALSE;
+}
+#endif /* FEAT_GUI_W32 */
+
+
+#ifdef FEAT_GUI_W32
+    void
+mch_write(
+    char_u  *s,
+    int	    len)
+{
+    /* never used */
+}
+
+#else
+
+/*
+ * clear `n' chars, starting from `coord'
+ */
+    static void
+clear_chars(
+    COORD coord,
+    DWORD n)
+{
+    DWORD dwDummy;
+
+    FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
+    FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy);
+}
+
+
+/*
+ * Clear the screen
+ */
+    static void
+clear_screen(void)
+{
+    g_coord.X = g_coord.Y = 0;
+    clear_chars(g_coord, Rows * Columns);
+}
+
+
+/*
+ * Clear to end of display
+ */
+    static void
+clear_to_end_of_display(void)
+{
+    clear_chars(g_coord, (Rows - g_coord.Y - 1)
+					   * Columns + (Columns - g_coord.X));
+}
+
+
+/*
+ * Clear to end of line
+ */
+    static void
+clear_to_end_of_line(void)
+{
+    clear_chars(g_coord, Columns - g_coord.X);
+}
+
+
+/*
+ * Scroll the scroll region up by `cLines' lines
+ */
+    static void
+scroll(
+    unsigned cLines)
+{
+    COORD oldcoord = g_coord;
+
+    gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
+    delete_lines(cLines);
+
+    g_coord = oldcoord;
+}
+
+
+/*
+ * Set the scroll region
+ */
+    static void
+set_scroll_region(
+    unsigned left,
+    unsigned top,
+    unsigned right,
+    unsigned bottom)
+{
+    if (left >= right
+	    || top >= bottom
+	    || right > (unsigned) Columns - 1
+	    || bottom > (unsigned) Rows - 1)
+	return;
+
+    g_srScrollRegion.Left =   left;
+    g_srScrollRegion.Top =    top;
+    g_srScrollRegion.Right =  right;
+    g_srScrollRegion.Bottom = bottom;
+}
+
+
+/*
+ * Insert `cLines' lines at the current cursor position
+ */
+    static void
+insert_lines(
+    unsigned cLines)
+{
+    SMALL_RECT	    source;
+    COORD	    dest;
+    CHAR_INFO	    fill;
+
+    dest.X = 0;
+    dest.Y = g_coord.Y + cLines;
+
+    source.Left   = 0;
+    source.Top	  = g_coord.Y;
+    source.Right  = g_srScrollRegion.Right;
+    source.Bottom = g_srScrollRegion.Bottom - cLines;
+
+    fill.Char.AsciiChar = ' ';
+    fill.Attributes = g_attrCurrent;
+
+    ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
+
+    /* Here we have to deal with a win32 console flake: If the scroll
+     * region looks like abc and we scroll c to a and fill with d we get
+     * cbd... if we scroll block c one line at a time to a, we get cdd...
+     * vim expects cdd consistently... So we have to deal with that
+     * here... (this also occurs scrolling the same way in the other
+     * direction).  */
+
+    if (source.Bottom < dest.Y)
+    {
+	COORD coord;
+
+	coord.X = 0;
+	coord.Y = source.Bottom;
+	clear_chars(coord, Columns * (dest.Y - source.Bottom));
+    }
+}
+
+
+/*
+ * Delete `cLines' lines at the current cursor position
+ */
+    static void
+delete_lines(
+    unsigned cLines)
+{
+    SMALL_RECT	    source;
+    COORD	    dest;
+    CHAR_INFO	    fill;
+    int		    nb;
+
+    dest.X = 0;
+    dest.Y = g_coord.Y;
+
+    source.Left   = 0;
+    source.Top	  = g_coord.Y + cLines;
+    source.Right  = g_srScrollRegion.Right;
+    source.Bottom = g_srScrollRegion.Bottom;
+
+    fill.Char.AsciiChar = ' ';
+    fill.Attributes = g_attrCurrent;
+
+    ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
+
+    /* Here we have to deal with a win32 console flake: If the scroll
+     * region looks like abc and we scroll c to a and fill with d we get
+     * cbd... if we scroll block c one line at a time to a, we get cdd...
+     * vim expects cdd consistently... So we have to deal with that
+     * here... (this also occurs scrolling the same way in the other
+     * direction).  */
+
+    nb = dest.Y + (source.Bottom - source.Top) + 1;
+
+    if (nb < source.Top)
+    {
+	COORD coord;
+
+	coord.X = 0;
+	coord.Y = nb;
+	clear_chars(coord, Columns * (source.Top - nb));
+    }
+}
+
+
+/*
+ * Set the cursor position
+ */
+    static void
+gotoxy(
+    unsigned x,
+    unsigned y)
+{
+    if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows)
+	return;
+
+    /* external cursor coords are 1-based; internal are 0-based */
+    g_coord.X = x - 1;
+    g_coord.Y = y - 1;
+    SetConsoleCursorPosition(g_hConOut, g_coord);
+}
+
+
+/*
+ * Set the current text attribute = (foreground | background)
+ * See ../doc/os_win32.txt for the numbers.
+ */
+    static void
+textattr(
+    WORD wAttr)
+{
+    g_attrCurrent = wAttr;
+
+    SetConsoleTextAttribute(g_hConOut, wAttr);
+}
+
+
+    static void
+textcolor(
+    WORD wAttr)
+{
+    g_attrCurrent = (g_attrCurrent & 0xf0) + wAttr;
+
+    SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
+}
+
+
+    static void
+textbackground(
+    WORD wAttr)
+{
+    g_attrCurrent = (g_attrCurrent & 0x0f) + (wAttr << 4);
+
+    SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
+}
+
+
+/*
+ * restore the default text attribute (whatever we started with)
+ */
+    static void
+normvideo()
+{
+    textattr(g_attrDefault);
+}
+
+
+static WORD g_attrPreStandout = 0;
+
+/*
+ * Make the text standout, by brightening it
+ */
+    static void
+standout(void)
+{
+    g_attrPreStandout = g_attrCurrent;
+    textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
+}
+
+
+/*
+ * Turn off standout mode
+ */
+    static void
+standend()
+{
+    if (g_attrPreStandout)
+    {
+	textattr(g_attrPreStandout);
+	g_attrPreStandout = 0;
+    }
+}
+
+
+/*
+ * Set normal fg/bg color, based on T_ME.  Called whem t_me has been set.
+ */
+    void
+mch_set_normal_colors()
+{
+    char_u	*p;
+    int		n;
+
+    cterm_normal_fg_color = (g_attrDefault & 0xf) + 1;
+    cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1;
+    if (T_ME[0] == ESC && T_ME[1] == '|')
+    {
+	p = T_ME + 2;
+	n = getdigits(&p);
+	if (*p == 'm' && n > 0)
+	{
+	    cterm_normal_fg_color = (n & 0xf) + 1;
+	    cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
+	}
+    }
+}
+
+
+/*
+ * visual bell: flash the screen
+ */
+    static void
+visual_bell()
+{
+    COORD   coordOrigin = {0, 0};
+    WORD    attrFlash = ~g_attrCurrent & 0xff;
+
+    DWORD   dwDummy;
+    LPWORD  oldattrs = (LPWORD)alloc(Rows * Columns * sizeof(WORD));
+
+    if (oldattrs == NULL)
+	return;
+    ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
+			       coordOrigin, &dwDummy);
+    FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns,
+			       coordOrigin, &dwDummy);
+
+    Sleep(15);	    /* wait for 15 msec */
+    WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
+				coordOrigin, &dwDummy);
+    vim_free(oldattrs);
+}
+
+
+/*
+ * Make the cursor visible or invisible
+ */
+    static void
+cursor_visible(
+    BOOL fVisible)
+{
+    s_cursor_visible = fVisible;
+#ifdef MCH_CURSOR_SHAPE
+    mch_update_cursor();
+#endif
+}
+
+
+/*
+ * write `cchToWrite' characters in `pchBuf' to the screen
+ * Returns the number of characters actually written (at least one).
+ */
+    static BOOL
+write_chars(
+    LPCSTR pchBuf,
+    DWORD  cchToWrite)
+{
+    COORD coord = g_coord;
+    DWORD written;
+
+    FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cchToWrite,
+				coord, &written);
+    /* When writing fails or didn't write a single character, pretend one
+     * character was written, otherwise we get stuck. */
+    if (WriteConsoleOutputCharacter(g_hConOut, pchBuf, cchToWrite,
+				coord, &written) == 0
+	    || written == 0)
+	written = 1;
+
+    g_coord.X += (SHORT) written;
+
+    while (g_coord.X > g_srScrollRegion.Right)
+    {
+	g_coord.X -= (SHORT) Columns;
+	if (g_coord.Y < g_srScrollRegion.Bottom)
+	    g_coord.Y++;
+    }
+
+    gotoxy(g_coord.X + 1, g_coord.Y + 1);
+
+    return written;
+}
+
+
+/*
+ * mch_write(): write the output buffer to the screen, translating ESC
+ * sequences into calls to console output routines.
+ */
+    void
+mch_write(
+    char_u  *s,
+    int	    len)
+{
+    s[len] = NUL;
+
+    if (!term_console)
+    {
+	write(1, s, (unsigned)len);
+	return;
+    }
+
+    /* translate ESC | sequences into faked bios calls */
+    while (len--)
+    {
+	/* optimization: use one single write_chars for runs of text,
+	 * rather than once per character  It ain't curses, but it helps. */
+	DWORD  prefix = strcspn(s, "\n\r\b\a\033");
+
+	if (p_wd)
+	{
+	    WaitForChar(p_wd);
+	    if (prefix != 0)
+		prefix = 1;
+	}
+
+	if (prefix != 0)
+	{
+	    DWORD nWritten;
+
+	    nWritten = write_chars(s, prefix);
+#ifdef MCH_WRITE_DUMP
+	    if (fdDump)
+	    {
+		fputc('>', fdDump);
+		fwrite(s, sizeof(char_u), nWritten, fdDump);
+		fputs("<\n", fdDump);
+	    }
+#endif
+	    len -= (nWritten - 1);
+	    s += nWritten;
+	}
+	else if (s[0] == '\n')
+	{
+	    /* \n, newline: go to the beginning of the next line or scroll */
+	    if (g_coord.Y == g_srScrollRegion.Bottom)
+	    {
+		scroll(1);
+		gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
+	    }
+	    else
+	    {
+		gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
+	    }
+#ifdef MCH_WRITE_DUMP
+	    if (fdDump)
+		fputs("\\n\n", fdDump);
+#endif
+	    s++;
+	}
+	else if (s[0] == '\r')
+	{
+	    /* \r, carriage return: go to beginning of line */
+	    gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
+#ifdef MCH_WRITE_DUMP
+	    if (fdDump)
+		fputs("\\r\n", fdDump);
+#endif
+	    s++;
+	}
+	else if (s[0] == '\b')
+	{
+	    /* \b, backspace: move cursor one position left */
+	    if (g_coord.X > g_srScrollRegion.Left)
+		g_coord.X--;
+	    else if (g_coord.Y > g_srScrollRegion.Top)
+	    {
+		g_coord.X = g_srScrollRegion.Right;
+		g_coord.Y--;
+	    }
+	    gotoxy(g_coord.X + 1, g_coord.Y + 1);
+#ifdef MCH_WRITE_DUMP
+	    if (fdDump)
+		fputs("\\b\n", fdDump);
+#endif
+	    s++;
+	}
+	else if (s[0] == '\a')
+	{
+	    /* \a, bell */
+	    MessageBeep(0xFFFFFFFF);
+#ifdef MCH_WRITE_DUMP
+	    if (fdDump)
+		fputs("\\a\n", fdDump);
+#endif
+	    s++;
+	}
+	else if (s[0] == ESC && len >= 3-1 && s[1] == '|')
+	{
+#ifdef MCH_WRITE_DUMP
+	    char_u* old_s = s;
+#endif
+	    char_u* p;
+	    int arg1 = 0, arg2 = 0;
+
+	    switch (s[2])
+	    {
+	    /* one or two numeric arguments, separated by ';' */
+
+	    case '0': case '1': case '2': case '3': case '4':
+	    case '5': case '6': case '7': case '8': case '9':
+		p = s + 2;
+		arg1 = getdigits(&p);	    /* no check for length! */
+		if (p > s + len)
+		    break;
+
+		if (*p == ';')
+		{
+		    ++p;
+		    arg2 = getdigits(&p);   /* no check for length! */
+		    if (p > s + len)
+			break;
+
+		    if (*p == 'H')
+			gotoxy(arg2, arg1);
+		    else if (*p == 'r')
+			set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
+		}
+		else if (*p == 'A')
+		{
+		    /* move cursor up arg1 lines in same column */
+		    gotoxy(g_coord.X + 1,
+			   max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
+		}
+		else if (*p == 'C')
+		{
+		    /* move cursor right arg1 columns in same line */
+		    gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
+			   g_coord.Y + 1);
+		}
+		else if (*p == 'H')
+		{
+		    gotoxy(1, arg1);
+		}
+		else if (*p == 'L')
+		{
+		    insert_lines(arg1);
+		}
+		else if (*p == 'm')
+		{
+		    if (arg1 == 0)
+			normvideo();
+		    else
+			textattr((WORD) arg1);
+		}
+		else if (*p == 'f')
+		{
+		    textcolor((WORD) arg1);
+		}
+		else if (*p == 'b')
+		{
+		    textbackground((WORD) arg1);
+		}
+		else if (*p == 'M')
+		{
+		    delete_lines(arg1);
+		}
+
+		len -= p - s;
+		s = p + 1;
+		break;
+
+
+	    /* Three-character escape sequences */
+
+	    case 'A':
+		/* move cursor up one line in same column */
+		gotoxy(g_coord.X + 1,
+		       max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
+		goto got3;
+
+	    case 'B':
+		visual_bell();
+		goto got3;
+
+	    case 'C':
+		/* move cursor right one column in same line */
+		gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
+		       g_coord.Y + 1);
+		goto got3;
+
+	    case 'E':
+		termcap_mode_end();
+		goto got3;
+
+	    case 'F':
+		standout();
+		goto got3;
+
+	    case 'f':
+		standend();
+		goto got3;
+
+	    case 'H':
+		gotoxy(1, 1);
+		goto got3;
+
+	    case 'j':
+		clear_to_end_of_display();
+		goto got3;
+
+	    case 'J':
+		clear_screen();
+		goto got3;
+
+	    case 'K':
+		clear_to_end_of_line();
+		goto got3;
+
+	    case 'L':
+		insert_lines(1);
+		goto got3;
+
+	    case 'M':
+		delete_lines(1);
+		goto got3;
+
+	    case 'S':
+		termcap_mode_start();
+		goto got3;
+
+	    case 'V':
+		cursor_visible(TRUE);
+		goto got3;
+
+	    case 'v':
+		cursor_visible(FALSE);
+		goto got3;
+
+	    got3:
+		s += 3;
+		len -= 2;
+	    }
+
+#ifdef MCH_WRITE_DUMP
+	    if (fdDump)
+	    {
+		fputs("ESC | ", fdDump);
+		fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
+		fputc('\n', fdDump);
+	    }
+#endif
+	}
+	else
+	{
+	    /* Write a single character */
+	    DWORD nWritten;
+
+	    nWritten = write_chars(s, 1);
+#ifdef MCH_WRITE_DUMP
+	    if (fdDump)
+	    {
+		fputc('>', fdDump);
+		fwrite(s, sizeof(char_u), nWritten, fdDump);
+		fputs("<\n", fdDump);
+	    }
+#endif
+
+	    len -= (nWritten - 1);
+	    s += nWritten;
+	}
+    }
+
+#ifdef MCH_WRITE_DUMP
+    if (fdDump)
+	fflush(fdDump);
+#endif
+}
+
+#endif /* FEAT_GUI_W32 */
+
+
+/*
+ * Delay for half a second.
+ */
+    void
+mch_delay(
+    long    msec,
+    int	    ignoreinput)
+{
+#ifdef FEAT_GUI_W32
+    Sleep((int)msec);	    /* never wait for input */
+#else
+    if (ignoreinput)
+	Sleep((int)msec);
+    else
+	WaitForChar(msec);
+#endif
+}
+
+
+/*
+ * this version of remove is not scared by a readonly (backup) file
+ * Return 0 for success, -1 for failure.
+ */
+    int
+mch_remove(char_u *name)
+{
+#ifdef FEAT_MBYTE
+    WCHAR	*wn = NULL;
+    int		n;
+
+    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
+    {
+	wn = enc_to_ucs2(name, NULL);
+	if (wn != NULL)
+	{
+	    SetFileAttributesW(wn, FILE_ATTRIBUTE_NORMAL);
+	    n = DeleteFileW(wn) ? 0 : -1;
+	    vim_free(wn);
+	    if (n == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+		return n;
+	    /* Retry with non-wide function (for Windows 98). */
+	}
+    }
+#endif
+    SetFileAttributes(name, FILE_ATTRIBUTE_NORMAL);
+    return DeleteFile(name) ? 0 : -1;
+}
+
+
+/*
+ * check for an "interrupt signal": CTRL-break or CTRL-C
+ */
+    void
+mch_breakcheck()
+{
+#ifndef FEAT_GUI_W32	    /* never used */
+    if (g_fCtrlCPressed || g_fCBrkPressed)
+    {
+	g_fCtrlCPressed = g_fCBrkPressed = FALSE;
+	got_int = TRUE;
+    }
+#endif
+}
+
+
+/*
+ * How much memory is available?
+ * Return sum of available physical and page file memory.
+ */
+    long_u
+mch_avail_mem(
+    int special)
+{
+    MEMORYSTATUS	ms;
+
+    ms.dwLength = sizeof(MEMORYSTATUS);
+    GlobalMemoryStatus(&ms);
+    return (long_u) (ms.dwAvailPhys + ms.dwAvailPageFile);
+}
+
+#ifdef FEAT_MBYTE
+/*
+ * Same code as below, but with wide functions and no comments.
+ * Return 0 for success, non-zero for failure.
+ */
+    int
+mch_wrename(WCHAR *wold, WCHAR *wnew)
+{
+    WCHAR	*p;
+    int		i;
+    WCHAR	szTempFile[_MAX_PATH + 1];
+    WCHAR	szNewPath[_MAX_PATH + 1];
+    HANDLE	hf;
+
+    if (!mch_windows95())
+    {
+	p = wold;
+	for (i = 0; wold[i] != NUL; ++i)
+	    if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':')
+		    && wold[i + 1] != 0)
+		p = wold + i + 1;
+	if ((int)(wold + i - p) < 8 || p[6] != '~')
+	    return (MoveFileW(wold, wnew) == 0);
+    }
+
+    if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL)
+	return -1;
+    *p = NUL;
+
+    if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0)
+	return -2;
+
+    if (!DeleteFileW(szTempFile))
+	return -3;
+
+    if (!MoveFileW(wold, szTempFile))
+	return -4;
+
+    if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW,
+		    FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
+	return -5;
+    if (!CloseHandle(hf))
+	return -6;
+
+    if (!MoveFileW(szTempFile, wnew))
+    {
+	(void)MoveFileW(szTempFile, wold);
+	return -7;
+    }
+
+    DeleteFileW(szTempFile);
+
+    if (!DeleteFileW(wold))
+	return -8;
+
+    return 0;
+}
+#endif
+
+
+/*
+ * mch_rename() works around a bug in rename (aka MoveFile) in
+ * Windows 95: rename("foo.bar", "foo.bar~") will generate a
+ * file whose short file name is "FOO.BAR" (its long file name will
+ * be correct: "foo.bar~").  Because a file can be accessed by
+ * either its SFN or its LFN, "foo.bar" has effectively been
+ * renamed to "foo.bar", which is not at all what was wanted.  This
+ * seems to happen only when renaming files with three-character
+ * extensions by appending a suffix that does not include ".".
+ * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
+ *
+ * There is another problem, which isn't really a bug but isn't right either:
+ * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be
+ * "abcdef~1.txt" again.  This has been reported on Windows NT 4.0 with
+ * service pack 6.  Doesn't seem to happen on Windows 98.
+ *
+ * Like rename(), returns 0 upon success, non-zero upon failure.
+ * Should probably set errno appropriately when errors occur.
+ */
+    int
+mch_rename(
+    const char	*pszOldFile,
+    const char	*pszNewFile)
+{
+    char	szTempFile[_MAX_PATH+1];
+    char	szNewPath[_MAX_PATH+1];
+    char	*pszFilePart;
+    HANDLE	hf;
+#ifdef FEAT_MBYTE
+    WCHAR	*wold = NULL;
+    WCHAR	*wnew = NULL;
+    int		retval = -1;
+
+    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
+    {
+	wold = enc_to_ucs2((char_u *)pszOldFile, NULL);
+	wnew = enc_to_ucs2((char_u *)pszNewFile, NULL);
+	if (wold != NULL && wnew != NULL)
+	    retval = mch_wrename(wold, wnew);
+	vim_free(wold);
+	vim_free(wnew);
+	if (retval == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+	    return retval;
+	/* Retry with non-wide function (for Windows 98). */
+    }
+#endif
+
+    /*
+     * No need to play tricks if not running Windows 95, unless the file name
+     * contains a "~" as the seventh character.
+     */
+    if (!mch_windows95())
+    {
+	pszFilePart = (char *)gettail((char_u *)pszOldFile);
+	if (STRLEN(pszFilePart) < 8 || pszFilePart[6] != '~')
+	    return rename(pszOldFile, pszNewFile);
+    }
+
+    /* Get base path of new file name.  Undocumented feature: If pszNewFile is
+     * a directory, no error is returned and pszFilePart will be NULL. */
+    if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0
+	    || pszFilePart == NULL)
+	return -1;
+    *pszFilePart = NUL;
+
+    /* Get (and create) a unique temporary file name in directory of new file */
+    if (GetTempFileName(szNewPath, "VIM", 0, szTempFile) == 0)
+	return -2;
+
+    /* blow the temp file away */
+    if (!DeleteFile(szTempFile))
+	return -3;
+
+    /* rename old file to the temp file */
+    if (!MoveFile(pszOldFile, szTempFile))
+	return -4;
+
+    /* now create an empty file called pszOldFile; this prevents the operating
+     * system using pszOldFile as an alias (SFN) if we're renaming within the
+     * same directory.  For example, we're editing a file called
+     * filename.asc.txt by its SFN, filena~1.txt.  If we rename filena~1.txt
+     * to filena~1.txt~ (i.e., we're making a backup while writing it), the
+     * SFN for filena~1.txt~ will be filena~1.txt, by default, which will
+     * cause all sorts of problems later in buf_write.  So, we create an empty
+     * file called filena~1.txt and the system will have to find some other
+     * SFN for filena~1.txt~, such as filena~2.txt
+     */
+    if ((hf = CreateFile(pszOldFile, GENERIC_WRITE, 0, NULL, CREATE_NEW,
+		    FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
+	return -5;
+    if (!CloseHandle(hf))
+	return -6;
+
+    /* rename the temp file to the new file */
+    if (!MoveFile(szTempFile, pszNewFile))
+    {
+	/* Renaming failed.  Rename the file back to its old name, so that it
+	 * looks like nothing happened. */
+	(void)MoveFile(szTempFile, pszOldFile);
+
+	return -7;
+    }
+
+    /* Seems to be left around on Novell filesystems */
+    DeleteFile(szTempFile);
+
+    /* finally, remove the empty old file */
+    if (!DeleteFile(pszOldFile))
+	return -8;
+
+    return 0;	/* success */
+}
+
+/*
+ * Get the default shell for the current hardware platform
+ */
+    char *
+default_shell()
+{
+    char* psz = NULL;
+
+    PlatformId();
+
+    if (g_PlatformId == VER_PLATFORM_WIN32_NT)		/* Windows NT */
+	psz = "cmd.exe";
+    else if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS) /* Windows 95 */
+	psz = "command.com";
+
+    return psz;
+}
+
+/*
+ * mch_access() extends access() to do more detailed check on network drives.
+ * Returns 0 if file "n" has access rights according to "p", -1 otherwise.
+ */
+    int
+mch_access(char *n, int p)
+{
+    HANDLE	hFile;
+    DWORD	am;
+    int		retval = -1;	    /* default: fail */
+#ifdef FEAT_MBYTE
+    WCHAR	*wn = NULL;
+
+    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
+	wn = enc_to_ucs2(n, NULL);
+#endif
+
+    if (mch_isdir(n))
+    {
+	char TempName[_MAX_PATH + 16] = "";
+#ifdef FEAT_MBYTE
+	WCHAR TempNameW[_MAX_PATH + 16] = L"";
+#endif
+
+	if (p & R_OK)
+	{
+	    /* Read check is performed by seeing if we can do a find file on
+	     * the directory for any file. */
+#ifdef FEAT_MBYTE
+	    if (wn != NULL)
+	    {
+		int		    i;
+		WIN32_FIND_DATAW    d;
+
+		for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i)
+		    TempNameW[i] = wn[i];
+		if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/')
+		    TempNameW[i++] = '\\';
+		TempNameW[i++] = '*';
+		TempNameW[i++] = 0;
+
+		hFile = FindFirstFileW(TempNameW, &d);
+		if (hFile == INVALID_HANDLE_VALUE)
+		{
+		    if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+			goto getout;
+
+		    /* Retry with non-wide function (for Windows 98). */
+		    vim_free(wn);
+		    wn = NULL;
+		}
+		else
+		    (void)FindClose(hFile);
+	    }
+	    if (wn == NULL)
+#endif
+	    {
+		char		    *pch;
+		WIN32_FIND_DATA	    d;
+
+		STRNCPY(TempName, n, _MAX_PATH);
+		pch = TempName + STRLEN(TempName) - 1;
+		if (*pch != '\\' && *pch != '/')
+		    *++pch = '\\';
+		*++pch = '*';
+		*++pch = NUL;
+
+		hFile = FindFirstFile(TempName, &d);
+		if (hFile == INVALID_HANDLE_VALUE)
+		    goto getout;
+		(void)FindClose(hFile);
+	    }
+	}
+
+	if (p & W_OK)
+	{
+	    /* Trying to create a temporary file in the directory should catch
+	     * directories on read-only network shares.  However, in
+	     * directories whose ACL allows writes but denies deletes will end
+	     * up keeping the temporary file :-(. */
+#ifdef FEAT_MBYTE
+	    if (wn != NULL)
+	    {
+		if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW))
+		{
+		    if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+			goto getout;
+
+		    /* Retry with non-wide function (for Windows 98). */
+		    vim_free(wn);
+		    wn = NULL;
+		}
+		else
+		    DeleteFileW(TempNameW);
+	    }
+	    if (wn == NULL)
+#endif
+	    {
+		if (!GetTempFileName(n, "VIM", 0, TempName))
+		    goto getout;
+		mch_remove((char_u *)TempName);
+	    }
+	}
+    }
+    else
+    {
+	/* Trying to open the file for the required access does ACL, read-only
+	 * network share, and file attribute checks.  */
+	am = ((p & W_OK) ? GENERIC_WRITE : 0)
+		| ((p & R_OK) ? GENERIC_READ : 0);
+#ifdef FEAT_MBYTE
+	if (wn != NULL)
+	{
+	    hFile = CreateFileW(wn, am, 0, NULL, OPEN_EXISTING, 0, NULL);
+	    if (hFile == INVALID_HANDLE_VALUE
+			      && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+	    {
+		/* Retry with non-wide function (for Windows 98). */
+		vim_free(wn);
+		wn = NULL;
+	    }
+	}
+	if (wn == NULL)
+#endif
+	    hFile = CreateFile(n, am, 0, NULL, OPEN_EXISTING, 0, NULL);
+	if (hFile == INVALID_HANDLE_VALUE)
+	    goto getout;
+	CloseHandle(hFile);
+    }
+
+    retval = 0;	    /* success */
+getout:
+#ifdef FEAT_MBYTE
+    vim_free(wn);
+#endif
+    return retval;
+}
+
+#if defined(FEAT_MBYTE) || defined(PROTO)
+/*
+ * Version of open() that may use ucs2 file name.
+ */
+    int
+mch_open(char *name, int flags, int mode)
+{
+    WCHAR	*wn;
+    int		f;
+
+    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
+# ifdef __BORLANDC__
+	    /* Wide functions of Borland C 5.5 do not work on Windows 98. */
+	    && g_PlatformId == VER_PLATFORM_WIN32_NT
+# endif
+       )
+    {
+	wn = enc_to_ucs2(name, NULL);
+	if (wn != NULL)
+	{
+	    f = _wopen(wn, flags, mode);
+	    vim_free(wn);
+	    if (f >= 0)
+		return f;
+	    /* Retry with non-wide function (for Windows 98). Can't use
+	     * GetLastError() here and it's unclear what errno gets set to if
+	     * the _wopen() fails for missing wide functions. */
+	}
+    }
+
+    return open(name, flags, mode);
+}
+
+/*
+ * Version of fopen() that may use ucs2 file name.
+ */
+    FILE *
+mch_fopen(char *name, char *mode)
+{
+    WCHAR	*wn, *wm;
+    FILE	*f = NULL;
+
+    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
+# ifdef __BORLANDC__
+	    /* Wide functions of Borland C 5.5 do not work on Windows 98. */
+	    && g_PlatformId == VER_PLATFORM_WIN32_NT
+# endif
+       )
+    {
+	wn = enc_to_ucs2(name, NULL);
+	wm = enc_to_ucs2(mode, NULL);
+	if (wn != NULL && wm != NULL)
+	    f = _wfopen(wn, wm);
+	vim_free(wn);
+	vim_free(wm);
+	if (f != NULL)
+	    return f;
+	/* Retry with non-wide function (for Windows 98). Can't use
+	 * GetLastError() here and it's unclear what errno gets set to if
+	 * the _wfopen() fails for missing wide functions. */
+    }
+
+    return fopen(name, mode);
+}
+#endif
+
+#ifdef FEAT_MBYTE
+/*
+ * SUB STREAM (aka info stream) handling:
+ *
+ * NTFS can have sub streams for each file.  Normal contents of file is
+ * stored in the main stream, and extra contents (author information and
+ * title and so on) can be stored in sub stream.  After Windows 2000, user
+ * can access and store those informations in sub streams via explorer's
+ * property menuitem in right click menu.  Those informations in sub streams
+ * were lost when copying only the main stream.  So we have to copy sub
+ * streams.
+ *
+ * Incomplete explanation:
+ *	http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp
+ * More useful info and an example:
+ *	http://www.sysinternals.com/ntw2k/source/misc.shtml#streams
+ */
+
+/*
+ * Copy info stream data "substream".  Read from the file with BackupRead(sh)
+ * and write to stream "substream" of file "to".
+ * Errors are ignored.
+ */
+    static void
+copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len)
+{
+    HANDLE  hTo;
+    WCHAR   *to_name;
+
+    to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR));
+    wcscpy(to_name, to);
+    wcscat(to_name, substream);
+
+    hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
+						 FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hTo != INVALID_HANDLE_VALUE)
+    {
+	long	done;
+	DWORD	todo;
+	DWORD	readcnt, written;
+	char	buf[4096];
+
+	/* Copy block of bytes at a time.  Abort when something goes wrong. */
+	for (done = 0; done < len; done += written)
+	{
+	    /* (size_t) cast for Borland C 5.5 */
+	    todo = (size_t)(len - done) > sizeof(buf) ? sizeof(buf)
+						       : (size_t)(len - done);
+	    if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt,
+						       FALSE, FALSE, context)
+		    || readcnt != todo
+		    || !WriteFile(hTo, buf, todo, &written, NULL)
+		    || written != todo)
+		break;
+	}
+	CloseHandle(hTo);
+    }
+
+    free(to_name);
+}
+
+/*
+ * Copy info streams from file "from" to file "to".
+ */
+    static void
+copy_infostreams(char_u *from, char_u *to)
+{
+    WCHAR		*fromw;
+    WCHAR		*tow;
+    HANDLE		sh;
+    WIN32_STREAM_ID	sid;
+    int			headersize;
+    WCHAR		streamname[_MAX_PATH];
+    DWORD		readcount;
+    void		*context = NULL;
+    DWORD		lo, hi;
+    int			len;
+
+    /* Convert the file names to wide characters. */
+    fromw = enc_to_ucs2(from, NULL);
+    tow = enc_to_ucs2(to, NULL);
+    if (fromw != NULL && tow != NULL)
+    {
+	/* Open the file for reading. */
+	sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL,
+			     OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+	if (sh != INVALID_HANDLE_VALUE)
+	{
+	    /* Use BackupRead() to find the info streams.  Repeat until we
+	     * have done them all.*/
+	    for (;;)
+	    {
+		/* Get the header to find the length of the stream name.  If
+		 * the "readcount" is zero we have done all info streams. */
+		ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
+		headersize = (char *)&sid.cStreamName - (char *)&sid.dwStreamId;
+		if (!BackupRead(sh, (LPBYTE)&sid, headersize,
+					   &readcount, FALSE, FALSE, &context)
+			|| readcount == 0)
+		    break;
+
+		/* We only deal with streams that have a name.  The normal
+		 * file data appears to be without a name, even though docs
+		 * suggest it is called "::$DATA". */
+		if (sid.dwStreamNameSize > 0)
+		{
+		    /* Read the stream name. */
+		    if (!BackupRead(sh, (LPBYTE)streamname,
+							 sid.dwStreamNameSize,
+					  &readcount, FALSE, FALSE, &context))
+			break;
+
+		    /* Copy an info stream with a name ":anything:$DATA".
+		     * Skip "::$DATA", it has no stream name (examples suggest
+		     * it might be used for the normal file contents).
+		     * Note that BackupRead() counts bytes, but the name is in
+		     * wide characters. */
+		    len = readcount / sizeof(WCHAR);
+		    streamname[len] = 0;
+		    if (len > 7 && wcsicmp(streamname + len - 6,
+							      L":$DATA") == 0)
+		    {
+			streamname[len - 6] = 0;
+			copy_substream(sh, &context, tow, streamname,
+						      (long)sid.Size.LowPart);
+		    }
+		}
+
+		/* Advance to the next stream.  We might try seeking too far,
+		 * but BackupSeek() doesn't skip over stream borders, thus
+		 * that's OK. */
+		(void)BackupSeek(sh, sid.Size.LowPart, sid.Size.HighPart,
+							  &lo, &hi, &context);
+	    }
+
+	    /* Clear the context. */
+	    (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context);
+
+	    CloseHandle(sh);
+	}
+    }
+    vim_free(fromw);
+    vim_free(tow);
+}
+#endif
+
+/*
+ * Copy file attributes from file "from" to file "to".
+ * For Windows NT and later we copy info streams.
+ * Always returns zero, errors are ignored.
+ */
+    int
+mch_copy_file_attribute(char_u *from, char_u *to)
+{
+#ifdef FEAT_MBYTE
+    /* File streams only work on Windows NT and later. */
+    PlatformId();
+    if (g_PlatformId == VER_PLATFORM_WIN32_NT)
+	copy_infostreams(from, to);
+#endif
+    return 0;
+}
+
+#if defined(MYRESETSTKOFLW) || defined(PROTO)
+/*
+ * Recreate a destroyed stack guard page in win32.
+ * Written by Benjamin Peterson.
+ */
+
+/* These magic numbers are from the MS header files */
+#define MIN_STACK_WIN9X 17
+#define MIN_STACK_WINNT 2
+
+/*
+ * This function does the same thing as _resetstkoflw(), which is only
+ * available in DevStudio .net and later.
+ * Returns 0 for failure, 1 for success.
+ */
+    int
+myresetstkoflw(void)
+{
+    BYTE	*pStackPtr;
+    BYTE	*pGuardPage;
+    BYTE	*pStackBase;
+    BYTE	*pLowestPossiblePage;
+    MEMORY_BASIC_INFORMATION mbi;
+    SYSTEM_INFO si;
+    DWORD	nPageSize;
+    DWORD	dummy;
+
+    /* This code will not work on win32s. */
+    PlatformId();
+    if (g_PlatformId == VER_PLATFORM_WIN32s)
+	return 0;
+
+    /* We need to know the system page size. */
+    GetSystemInfo(&si);
+    nPageSize = si.dwPageSize;
+
+    /* ...and the current stack pointer */
+    pStackPtr = (BYTE*)_alloca(1);
+
+    /* ...and the base of the stack. */
+    if (VirtualQuery(pStackPtr, &mbi, sizeof mbi) == 0)
+	return 0;
+    pStackBase = (BYTE*)mbi.AllocationBase;
+
+    /* ...and the page thats min_stack_req pages away from stack base; this is
+     * the lowest page we could use. */
+    pLowestPossiblePage = pStackBase + ((g_PlatformId == VER_PLATFORM_WIN32_NT)
+			     ? MIN_STACK_WINNT : MIN_STACK_WIN9X) * nPageSize;
+
+    /* On Win95, we want the next page down from the end of the stack. */
+    if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS)
+    {
+	/* Find the page that's only 1 page down from the page that the stack
+	 * ptr is in. */
+	pGuardPage = (BYTE*)((DWORD)nPageSize * (((DWORD)pStackPtr
+						    / (DWORD)nPageSize) - 1));
+	if (pGuardPage < pLowestPossiblePage)
+	    return 0;
+
+	/* Apply the noaccess attribute to the page -- there's no guard
+	 * attribute in win95-type OSes. */
+	if (!VirtualProtect(pGuardPage, nPageSize, PAGE_NOACCESS, &dummy))
+	    return 0;
+    }
+    else
+    {
+	/* On NT, however, we want the first committed page in the stack Start
+	 * at the stack base and move forward through memory until we find a
+	 * committed block. */
+	BYTE *pBlock = pStackBase;
+
+	while (1)
+	{
+	    if (VirtualQuery(pBlock, &mbi, sizeof mbi) == 0)
+		return 0;
+
+	    pBlock += mbi.RegionSize;
+
+	    if (mbi.State & MEM_COMMIT)
+		break;
+	}
+
+	/* mbi now describes the first committed block in the stack. */
+	if (mbi.Protect & PAGE_GUARD)
+	    return 1;
+
+	/* decide where the guard page should start */
+	if ((long_u)(mbi.BaseAddress) < (long_u)pLowestPossiblePage)
+	    pGuardPage = pLowestPossiblePage;
+	else
+	    pGuardPage = (BYTE*)mbi.BaseAddress;
+
+	/* allocate the guard page */
+	if (!VirtualAlloc(pGuardPage, nPageSize, MEM_COMMIT, PAGE_READWRITE))
+	    return 0;
+
+	/* apply the guard attribute to the page */
+	if (!VirtualProtect(pGuardPage, nPageSize, PAGE_READWRITE | PAGE_GUARD,
+								      &dummy))
+	    return 0;
+    }
+
+    return 1;
+}
+
+#endif