ftp.nice.ch/pub/next/unix/editor/vim-5.0f.s.tar.gz#/vim-5.0f/src/os_win32.c

This is os_win32.c in view mode; [Download] [Up]

/* vi:set ts=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.
 */

/*
 * os_win32.c
 *
 * 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 <gvr@halcyon.com> wrote most of this.
 * Roger Knobbe <rogerk@wonderware.com> did the initial port of Vim 3.0.
 */

#include <io.h>
#include "vim.h"
#include "globals.h"
#include "option.h"
#include "proto.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>
#include <time.h>

#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#undef chdir
#include <direct.h>

#if 0
#include <stdarg.h>
#endif


/* Disgusting hack for getting dead keys to work properly on Windows 95.
 * See below for the gory details.  If you're only running on NT or
 * if you're an English-speaking user, you can comment this out. */

/* WARNING: this code is experimental and incomplete and it does not work.
 * Don't use it! */
/* #define WIN95_DEAD_KEYS_HACK */


/* Force all filenames to lowercase */
/* #define DOWNCASE_FILENAMES */


/* Record all output and all keyboard & mouse input */
/* #define MCH_WRITE_DUMP */

#ifdef MCH_WRITE_DUMP
FILE* fdDump = NULL;
#endif /* MCH_WRITE_DUMP */


/*
 * When generating prototypes for Win32 on Unix, these lines make the syntax
 * errors disappear.  They do not need to be correct.
 */
#ifdef PROTO
# define HANDLE int
# define SMALL_RECT int
# define COORD int
# define SHORT int
# define WORD int
# define DWORD int
# define BOOL int
# define LPSTR int
# define KEY_EVENT_RECORD int
# define MOUSE_EVENT_RECORD int
# define WINAPI
# define CONSOLE_CURSOR_INFO int
# define LPCSTR char_u *
# define WINBASEAPI
#endif

/* 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); */
#ifdef __BORLANDC__
typedef BOOL (*PFNGCKLN)(LPSTR);
#else
typedef WINBASEAPI BOOL (*PFNGCKLN)(LPSTR);
#endif
PFNGCKLN	s_pfnGetConsoleKeyboardLayoutName = NULL;

#ifdef __BORLANDC__
// being a more ANSI compliant compiler, BorlandC doesn't define _stricoll:

int _stricoll(char *a, char *b)
{
#if 1
	// this is fast but not correct:
	return stricmp(a,b);
#else
	// the ANSI-ish correct way is to use strxfrm():
	char a_buff[512], b_buff[512];	// file names, so this is enough on Win32
	strxfrm(a_buff, a, 512);
	strxfrm(b_buff, b, 512);
	return strcoll(a_buff, b_buff);
#endif
}
#endif

/* Win32 Console handles for input and output */
static HANDLE g_hConIn  = INVALID_HANDLE_VALUE;
static HANDLE g_hSavOut = INVALID_HANDLE_VALUE;
static HANDLE g_hCurOut = 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 void termcap_mode_start();
static void termcap_mode_end();
static void clear_chars(COORD coord, DWORD n);
static void clear_screen();
static void clear_to_end_of_display();
static void clear_to_end_of_line();
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();
static void textattr(WORD wAttr);
static void textcolor(WORD wAttr);
static void textbackground(WORD wAttr);
static void standout();
static void standend();
static void visual_bell();
static void cursor_visible(BOOL fVisible);
static BOOL write_chars(LPCSTR pchBuf, DWORD cchToWrite, DWORD* pcchWritten);


/* 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

static DWORD g_PlatformId;

/*
 *	Returns VER_PLATFORM_WIN32_NT (NT) or VER_PLATFORM_WIN32_WINDOWS (Win95)
 */
	static DWORD
PlatformId()
{
	OSVERSIONINFO ovi;

	ovi.dwOSVersionInfoSize = sizeof(ovi);
	GetVersionEx(&ovi);

	g_PlatformId = ovi.dwPlatformId;

	return g_PlatformId;
}

#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)


#ifdef WIN95_DEAD_KEYS_HACK

/* The problem that this code attempts to work around is that the
 * console-mode input routines are broken on Windows 95 if you're
 * using dead keys.  A dead key is an accent key, such as acute,
 * grave, or umlaut, that doesn't produce a character by itself,
 * but when followed by another key, produces an accented character,
 * such as a-acute, e-grave, u-umlaut, n-tilde, and so on.  Very
 * useful for most European languages.  English-language keyboard
 * layouts don't use dead keys, as far as I know.
 *
 * Unfortunately, the KEY_EVENT_RECORD contains the wrong data when
 * I get the keystroke that's supposed to have an accented character.
 * The AsciiChar part contains the unaccented character instead.
 *
 * The following code (unsuccessfully) attempts to fake the dead keys.
 * I rely on the fact that the virtual key code and the scan key code
 * are actually correct for both the dead key and the following letter.
 * I set up a second thread that creates a hidden window and then sits
 * in a little message loop.  I send it all the data from the dead key
 * and the following key (each thread has its own keyboard input state),
 * and it sends its own hidden window a suitable sequence of WM_KEYDOWN
 * and WM_KEYUP messages.  TranslateMessage causes these to be turned
 * into WM_DEADCHAR and WM_CHAR messages.  Almost.  It works as long as
 * neither the dead key nor the following key are shifted, but after
 * happens, that it goes to hell in a handbasket.
 *
 * I have spent many, many hours trying all sorts of approaches to get
 * dead keys to work with Windows 95 and I am completely stumped.
 *
 * So why not just have all the input come directly via WM_CHAR?
 * Because this is a console application and I don't know how to do that.
 *
 * So how do you type dead keys?  Answer: you don't, not if you're using
 * Windows 95, not until either Microsoft fixes the bug (and they haven't
 * as of Service Pack 1) or someone (me?) produces a true Windows GUI
 * version of Vim.
 *
 * If someone can come up with some workaround, please, please, please
 * send it to me.
 *
 * You can, however, use digraphs.  They're not as convenient as dead
 * keys, especially if you're used to typing dead keys, but they do
 * work and they're better than nothing.
 *
 * If you're using NT, the dead keys work fine.  At least they do with
 * NT 3.51, Service Pack 3, US Edition.  I haven't tested it with any
 * other version of NT.  I did hear one report that NT3.51, SP3, UK edition
 * had problems with CTRL-vowels, but I don't know about dead keys.
 *
 * /George V. Reilly, 3/15/1996
 *
 * On 24th March, I was told by a senior developer at Microsoft:
 *   Win95 console support has always been and will always be flaky.
 *
 *   The flakiness is unavoidable because we are stuck between the world of
 *   MS-DOS keyboard TSRs like KEYB (which wants to cook the data; important
 *   for international) and the world of Win32.
 *
 *   So keys that don't "exist" in MS-DOS land (like dead keys) have a
 *   very tenuous existence in Win32 console land.  Keys that act
 *   differently between MS-DOS land and Win32 console land (like capslock)
 *   will act flaky.
 *
 *   Don't even *mention* the problems with multiple language keyboard
 *   layouts...
 */

static HWND   g_hwndKbd  = NULL;
static HANDLE g_hthrdKbd = NULL;
static DWORD  g_dwThreadId = 0;

#define WM_SENDDEADKEY (WM_USER+31)


void send_key(UINT uVirtKey, UINT uScanCode, UINT fDown);
void key_down(UINT uVirtKey, UINT uScanCode);
void keystate_down(UINT uCtrlState);
void key_up(UINT uVirtKey, UINT uScanCode);
void keystate_up(UINT uCtrlState);

	static LRESULT CALLBACK
keyboard_wndproc(
	HWND hwnd,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam)
{
	static int n, vk, sc;
	char ch, sz[200];

	sprintf(sz, "kbd: msg = %04x, w = %08x, l = %08x\n", uMsg, wParam, lParam);
	OutputDebugString(sz);

	switch (uMsg)
	{
	case WM_SENDDEADKEY:
		{
			UINT uDeadVirt, uDeadScan, uDeadCtrl, uVirt, uScan, uCtrl;
			BYTE abKeystate[256];
			WORD awAnsiCode[2];

			uDeadVirt = LOBYTE(wParam);
			uDeadScan = HIBYTE(wParam);
			uDeadCtrl = HIWORD(wParam);

			uVirt = LOBYTE(lParam);
			uScan = HIBYTE(lParam);
			uCtrl = HIWORD(lParam);

			GetKeyboardState(abKeystate);

			memset(abKeystate, 0, sizeof (abKeystate));
			if (!SetKeyboardState(abKeystate))
			{
				n = GetLastError();
				sprintf(sz, "setkeyboardstate failed, error %d\n", n);
				OutputDebugString(sz);
			}

			ToAscii(VK_SPACE, MapVirtualKey(VK_SPACE, 0),
					abKeystate, awAnsiCode, 0);

			if (uDeadCtrl & SHIFT_PRESSED)
				abKeystate[VK_SHIFT] = 0x80;
			if (uDeadCtrl & CAPSLOCK_ON)
				abKeystate[VK_CAPITAL] = 1;

			if ((uDeadCtrl & ALT_GR) == ALT_GR)
			{
				abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
					abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
			}

			if (!SetKeyboardState(abKeystate))
			{
				n = GetLastError();
				sprintf(sz, "setkeyboardstate failed, error %d\n", n);
				OutputDebugString(sz);
			}

			//keystate_down(uDeadCtrl);
			key_down(uDeadVirt, uDeadScan);
			key_up(uDeadVirt, uDeadScan);
			//keystate_up(uDeadCtrl);

			memset(abKeystate, 0, sizeof (abKeystate));

			if (uCtrl & SHIFT_PRESSED)
				abKeystate[VK_SHIFT] = 0x80;
			if (uCtrl & CAPSLOCK_ON)
				abKeystate[VK_CAPITAL] = 1;

			if ((uCtrl & ALT_GR) == ALT_GR)
			{
				abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
					abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
			}

			if (!SetKeyboardState(abKeystate))
			{
				int n = GetLastError();
			}

			//keystate_down(uCtrl);
			key_down(uVirt, uScan);
			Sleep(30);

			key_up(uVirt, uScan);
			//keystate_up(uCtrl);
		}
		break;

	case WM_CHAR:
	case WM_DEADCHAR:
		ch = wParam;
		CharToOemBuff(&ch, &ch, 1);
		sprintf(sz, "WM_%sCHAR: wParam = 0x%2x, Oem = 0x%2x, '%c'\n",
				((uMsg == WM_CHAR) ? "" : "DEAD"),
				wParam, LOBYTE(ch), isprint(ch) ? ch : '?');
		OutputDebugString(sz);

		n = DefWindowProc(hwnd, uMsg, wParam, lParam);
		return n;

	case WM_KEYDOWN:
		vk = wParam;
		sc = HIWORD(lParam);

	default:
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
}



	static DWORD WINAPI
keyboard_threadproc(
	LPVOID lpvThreadParam)
{
	const char szKbdTrans[] = "VimKbdXlator";
	WNDCLASS wndclass;
	MSG msg;
	int i = 0;

	wndclass.style = 0;
	wndclass.lpfnWndProc = keyboard_wndproc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = GetModuleHandle(NULL);
	wndclass.hIcon = NULL;
	wndclass.hCursor = NULL;
	wndclass.hbrBackground = NULL;
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szKbdTrans;

	RegisterClass(&wndclass);

	g_hwndKbd = CreateWindow(szKbdTrans, "", 0,
							 CW_USEDEFAULT, CW_USEDEFAULT,
							 CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
							 GetModuleHandle(NULL), NULL);

	while (GetMessage(&msg, NULL, 0, 0))
	{
#if 0
		char sz[200];
		sprintf(sz, "gm: %2d: hwnd = %x, msg = %x, w = %x, l = %x\n",
				++i, msg.hwnd, msg.message, msg.wParam, msg.lParam);
		OutputDebugString(sz);
#endif

		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}



	static BOOL
keyboard_init()
{
	const char sz[] = "aA\xE0\xE1\xE8\xE9\xF1\xD1";
	const char* psz = sz;

	for ( ; *psz; psz++)
	{
		char sz2[100];
		SHORT w = VkKeyScan(*psz);

		sprintf(sz2, "%02x: %04x\n", *psz, w);
		OutputDebugString(sz2);
	}

	g_hthrdKbd = (HANDLE) _beginthreadex(NULL, 0, keyboard_threadproc,
										 (LPVOID) NULL, 0, &g_dwThreadId);

	return (g_hthrdKbd != NULL);
}



	static void
keyboard_exit()
{
	TerminateThread(g_hthrdKbd, 0);
}



	void
send_key(
	UINT uVirtKey,
	UINT uScanCode,
	UINT fDown)
{
	PostMessage(g_hwndKbd,
				fDown ? WM_KEYDOWN : WM_KEYUP,
				uVirtKey,
				MAKELONG(1, uScanCode | (fDown ? 0 : 0x8000)));
	Sleep(20);
}



	void
key_down(
	UINT uVirtKey,
	UINT uScanCode)
{
	send_key(uVirtKey, uScanCode, TRUE);
}



	void
keystate_down(
	UINT uCtrlState)
{
	if (uCtrlState & SHIFT_PRESSED)
		key_down(VK_SHIFT, MapVirtualKey(VK_SHIFT, 0));
	if ((uCtrlState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) != 0)
		key_down(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0));
}



	void
key_up(
	UINT uVirtKey,
	UINT uScanCode)
{
	send_key(uVirtKey, uScanCode, FALSE);
}



	void
keystate_up(
	UINT uCtrlState)
{
	if ((uCtrlState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) != 0)
		key_up(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0));
	if (uCtrlState & SHIFT_PRESSED)
		key_up(VK_SHIFT, MapVirtualKey(VK_SHIFT, 0));
}

#endif /* WIN95_DEAD_KEYS_HACK */



/* 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*/

	/* 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', },
};


// The ToAscii bug destroys several registers.  Need to turn off optimization
// or the GetConsoleKeyboardLayoutName hack will fail in non-debug versions

#pragma optimize("", off)


/* The return code indicates key code size. */
	static int
win32_kbd_patch_key(
	KEY_EVENT_RECORD* pker)
{
	static int s_iIsDead = 0;
	static WORD awAnsiCode[2];
	UINT uMods = pker->dwControlKeyState;
	BYTE abKeystate[256];

	if (s_iIsDead == 2)
	{
		pker->uChar.AsciiChar = (CHAR) awAnsiCode[1];
		s_iIsDead = 0;
		return 1;
	}

	if (pker->uChar.AsciiChar != 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))
		{
			HKL hkl = 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->uChar.AsciiChar = (CHAR) awAnsiCode[0];

	return s_iIsDead;
}


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* pchPending,
	BOOL fDoPost)
{
	int i;
	const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);

#ifdef WIN95_DEAD_KEYS_HACK
	static UINT uDeadVirtKey = 0, uDeadScan = 0, uDeadCtrlState = 0;
	char sz[200];

	sprintf(sz, "Vk = 0x%02x, `%c'; Scan = 0x%02x; "
			"Char = %3d, 0x%02x, `%c'; Ctrl = 0x%02x, Post = %d\n",
			pker->wVirtualKeyCode,
			isprint(pker->wVirtualKeyCode) ? pker->wVirtualKeyCode :'?',
			pker->wVirtualScanCode,
			LOBYTE(pker->uChar.AsciiChar), LOBYTE(pker->uChar.AsciiChar),
			isprint(pker->uChar.AsciiChar) ? pker->uChar.AsciiChar :'?',
			pker->dwControlKeyState, fDoPost);
	OutputDebugString(sz);
#endif /* WIN95_DEAD_KEYS_HACK */

	*pch = *pchPending = 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;
	}

#ifdef WIN95_DEAD_KEYS_HACK
	if (fDoPost  &&  uDeadVirtKey != 0)
	{
		sprintf(sz, "sending vk = 0x%2x, sc = 0x%2x\n",
				pker->wVirtualKeyCode, pker->wVirtualScanCode);
		OutputDebugString(sz);

		PostMessage(g_hwndKbd, WM_SENDDEADKEY,
					MAKELONG(MAKEWORD(uDeadVirtKey, uDeadScan), uDeadCtrlState),
					MAKELONG(MAKEWORD(pker->wVirtualKeyCode,
									  pker->wVirtualScanCode),
							 pker->dwControlKeyState));
	}
#endif /* WIN95_DEAD_KEYS_HACK */

#if 0
	/* If AltGr has been pressed, remove it.  */
	if ((pker->dwControlKeyState & ALT_GR) == ALT_GR)
		pker->dwControlKeyState &= ~ ALT_GR;
#endif

#if 0
	/* If CapsLock is on on Win95, digits will be shifted by default;
	 * e.g., pressing '1' will give '!'.  Feh. */
	if ((pker->dwControlKeyState & CAPSLOCK_ON) &&
		!(pker->dwControlKeyState & SHIFT_PRESSED) &&
		('0' <= pker->wVirtualKeyCode  &&  pker->wVirtualKeyCode <= '9'))
	{
		pker->uChar.AsciiChar = NUL;
	}
#endif

#if 0
	if ((*pch = pker->uChar.AsciiChar) != NUL)
		return TRUE;
#endif

	/* special cases */
	if ((nModifs & CTRL) != 0  &&  (nModifs & ~CTRL) == 0
		&&  pker->uChar.AsciiChar == NUL)
	{
		/* Ctrl-6 is Ctrl-^ */
		if (pker->wVirtualKeyCode == '6')
		{
			*pch = Ctrl('^');
			return TRUE;
		}
		/* Ctrl-2 is Ctrl-@ */
		else if (pker->wVirtualKeyCode == '2')
		{
			*pch = NUL;
			return TRUE;
		}
	}

	/* Shift-TAB */
	if (pker->wVirtualKeyCode == VK_TAB  &&  (nModifs & SHIFT_PRESSED))
	{
		*pch = K_NUL;
		*pchPending = '\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)
				{
					*pchPending = *pch;
					*pch = K_NUL;
				}

				return TRUE;
			}
		}
	}

	i = win32_kbd_patch_key(pker);

	if (i < 0)
	{
		*pch = NUL;

#ifdef WIN95_DEAD_KEYS_HACK
		if (fDoPost)
		{
			uDeadVirtKey = pker->wVirtualKeyCode;
			uDeadScan = pker->wVirtualScanCode;
			uDeadCtrlState = pker->dwControlKeyState;
		}
#endif /* WIN95_DEAD_KEYS_HACK */
	}
	else
	{
#ifdef WIN95_DEAD_KEYS_HACK
		uDeadVirtKey = uDeadScan = uDeadCtrlState = 0;
#endif /* WIN95_DEAD_KEYS_HACK */
		*pch = (i > 0)  ?  pker->uChar.AsciiChar  :  NUL;
	}

	return (*pch != NUL);
}

#pragma optimize("", on)


#ifdef USE_MOUSE

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;
	static int s_cClicks = 1;
	static BOOL s_fReleased = TRUE;
	static s_dwLastClickTime = 0;
	static BOOL s_fNextIsMiddle = FALSE;

	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 (! 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 == 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 */
	{
		const int cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);

		/* 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
				||  (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;

	/* 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;
	}

	g_nMouseClick |= 0x20;

	s_xOldMouse = g_xMouse;
	s_yOldMouse = g_yMouse;
	s_old_topline = curwin->w_topline;
	s_nOldMouseClick = g_nMouseClick;

	if (nButton != MOUSE_DRAG  &&  nButton != MOUSE_RELEASE)
		SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);

	return TRUE;
}


#endif /* USE_MOUSE */


/*
 * Wait until console input from keyboard or mouse is available,
 * or the time is up
 */
	static int
WaitForChar(
	long msec)
{
	DWORD dwNow, dwEndTime;

	if (msec <= 0)
		msec = 2;	/* very short time */

	dwEndTime = GetTickCount() + msec;

	/* We need to loop until the end of the time period, because
	 * we might get multiple unusable mouse events in that time.
	 */
	while ((dwNow = GetTickCount())  <  dwEndTime)
	{
		if (WaitForSingleObject(g_hConIn, dwEndTime - dwNow) == WAIT_OBJECT_0)
		{
			INPUT_RECORD ir;
			DWORD cRecords = 0;

			PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);

			if (cRecords > 0)
			{
				if (ir.EventType == KEY_EVENT  &&  ir.Event.KeyEvent.bKeyDown)
				{
					char_u ch, ch2;

					return decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
											FALSE);
				}
#ifdef USE_CLIPBOARD
				else if (ir.EventType == FOCUS_EVENT)
				{
					ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

					g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
					/* TRACE("WaitForChar: Focus %d\n", g_fJustGotFocus); */
					clip_mch_lose_focus();
				}
#endif
				else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
				{
					ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

					set_winsize(Rows, Columns, FALSE);
				}
#ifdef USE_MOUSE
				else if (ir.EventType == MOUSE_EVENT)
				{
					ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

					if (decode_mouse_event(&ir.Event.MouseEvent))
						return TRUE;
				}
#endif /* USE_MOUSE */
				else
				{
					/* Discard it, it's an insignificant event */
					ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
				}
			}
		}
	}

	return FALSE;
}


static char_u g_chPending = NUL;


/*
 * Is there a pending keystroke or mouse event?
 */
	static BOOL
kbhit()
{
	if (g_chPending != NUL
#ifdef USE_MOUSE
		|| g_nMouseClick != -1
#endif /* USE_MOUSE */
		)
		return TRUE;

	for ( ; ; )
	{
		INPUT_RECORD ir;
		DWORD cRecords = 0;

		PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);

		if (cRecords == 0)
			return FALSE;
		else
		{
			if (ir.EventType == KEY_EVENT  &&  ir.Event.KeyEvent.bKeyDown)
			{
				char_u ch, ch2;

				return decode_key_event(&ir.Event.KeyEvent, &ch, &ch2, FALSE);
			}
#ifdef USE_CLIPBOARD
			else if (ir.EventType == FOCUS_EVENT)
			{
				ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

				g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
				/* TRACE("kbhit: Focus %d\n", g_fJustGotFocus); */
				clip_mch_lose_focus();
			}
#endif
			else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
			{
				ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

				set_winsize(Rows, Columns, FALSE);
			}
#ifdef USE_MOUSE
			else if (ir.EventType == MOUSE_EVENT)
			{
				ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

				if (decode_mouse_event(&ir.Event.MouseEvent))
					return TRUE;
			}
#endif /* USE_MOUSE */
			else
			{
				/* Discard it, it's an insignificant event */
				ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
			}
		}
	}
}


/*
 * Get a keystroke or a mouse event
 */
	static char_u
tgetch()
{
	char_u ch;

	if (g_chPending != NUL)
	{
		ch = g_chPending;
		g_chPending = NUL;
		return ch;
	}

	for ( ; ; )
	{
		INPUT_RECORD ir;
		DWORD cRecords = 0;

		ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

		if (ir.EventType == KEY_EVENT)
		{
			if (decode_key_event(&ir.Event.KeyEvent, &ch, &g_chPending, TRUE))
				return ch;
		}
#ifdef USE_CLIPBOARD
		else if (ir.EventType == FOCUS_EVENT)
		{
			g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
			/* TRACE("tgetch: Focus %d\n", g_fJustGotFocus); */
			clip_mch_lose_focus();
		}
#endif
		else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
		{
			set_winsize(Rows, Columns, FALSE);
		}
#ifdef USE_MOUSE
		else if (ir.EventType == MOUSE_EVENT)
		{
			if (decode_mouse_event(&ir.Event.MouseEvent))
				return 0;
		}
#endif /* USE_MOUSE */
	}
}


/*
 * 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 len = 0;
	int c;

	if (time >= 0)
	{
		if (! WaitForChar(time))	 /* no character available */
			return 0;
	}
	else	/* time == -1, wait forever */
	{
		/* If there is no character available within 2 seconds (default),
		 * write the autoscript file to disk */
		if (WaitForChar(p_ut) == 0)
			updatescript(0);
	}

	/*
	 * Try to read as many characters as there are.
	 */

	--maxlen;				   /* may get two chars at once */

	/* 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 /* MCH_WRITE_DUMP */

	while ((len == 0 || kbhit())  &&  len < maxlen)
	{
#ifdef USE_MOUSE
		if (g_nMouseClick != -1  &&  maxlen >= 5-1)
		{
# ifdef MCH_WRITE_DUMP
			if (fdDump)
				fprintf(fdDump, "{%02x @ %d, %d}",
						g_nMouseClick, g_xMouse, g_yMouse);
# endif /* MCH_WRITE_DUMP */

			len = 5;
			*buf++ = ESC + 128;
			*buf++ = 'M';
			*buf++ = g_nMouseClick;
			*buf++ = g_xMouse + '!';
			*buf++ = g_yMouse + '!';
			g_nMouseClick = -1;

		}
		else
#endif /* USE_MOUSE */
		{
			if ((c = tgetch()) == Ctrl('C'))
				g_fCBrkPressed = TRUE;

#ifdef USE_MOUSE
			if (g_nMouseClick == -1)
#endif /* USE_MOUSE */
			{
				*buf++ = c;
				len++;

#ifdef MCH_WRITE_DUMP
				if (fdDump)
					fputc(c, fdDump);
#endif /* MCH_WRITE_DUMP */

			}
		}
	}

#ifdef MCH_WRITE_DUMP
	if (fdDump)
	{
		fputs("]\n", fdDump);
		fflush(fdDump);
	}
#endif /* MCH_WRITE_DUMP */

	beep_count = 0;			/* may beep again now that we got some chars */
	return len;
}


static char g_szOrigTitle[256];
static int  g_fWindInitCalled = FALSE;
static CONSOLE_CURSOR_INFO g_cci;
static DWORD g_cmodein = 0;
static DWORD g_cmodeout = 0;

/*
 * Because of a bug in the Windows 95 Console, we need to set the screen size
 * back when switching screens.
 */
static int g_nOldRows = 0;
static int g_nOldColumns = 0;

/*
 * platform-specific initialization
 */
	void
mch_windinit()
{
	CONSOLE_SCREEN_BUFFER_INFO csbi;
	extern int _fmode;

	PlatformId();

	_fmode = O_BINARY;			/* we do our own CR-LF translation */
	flushbuf();

	/* Obtain handles for the standard Console I/O devices */
	if (read_cmd_fd == 0)
		g_hConIn =  GetStdHandle(STD_INPUT_HANDLE);
	else
		/*
		 * TODO: Should get something else than stdin, but this doesn't seem
		 * to work:
		 */
		g_hConIn =  GetStdHandle(STD_OUTPUT_HANDLE);
	g_hSavOut = GetStdHandle(STD_OUTPUT_HANDLE);
	g_hCurOut = g_hSavOut;

	/* Get current text attributes */
	GetConsoleScreenBufferInfo(g_hSavOut, &csbi);
	g_attrCurrent = g_attrDefault = csbi.wAttributes;
	GetConsoleCursorInfo(g_hSavOut, &g_cci);

	GetConsoleMode(g_hConIn,  &g_cmodein);
	GetConsoleMode(g_hSavOut, &g_cmodeout);

	GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle));
	ui_get_winsize();

	g_nOldRows = Rows;
	g_nOldColumns = Columns;

#ifdef MCH_WRITE_DUMP
	fdDump = fopen("dump", "wt");

	if (fdDump)
	{
		time_t t;

		time(&t);
		fputs(ctime(&t), fdDump);
		fflush(fdDump);
	}
#endif /* MCH_WRITE_DUMP */

	g_fWindInitCalled = TRUE;

#ifdef USE_MOUSE
	g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
#endif

#ifdef USE_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.
	 */
	clipboard.format = RegisterClipboardFormat("VimClipboard");
#endif

#ifdef WIN95_DEAD_KEYS_HACK
	keyboard_init();
#endif

	/* This will be NULL on anything but NT 4.0 */
	s_pfnGetConsoleKeyboardLayoutName =
		(PFNGCKLN) GetProcAddress(GetModuleHandle("kernel32.dll"),
								  "GetConsoleKeyboardLayoutNameA");
	/*
	 * We don't really want to jump to our own screen yet; do that after
	 * starttermcap().  This flashes the window, sorry about that, but
	 * otherwise "vim -r" doesn't work.
	 */
	g_hCurOut = g_hSavOut;
	SetConsoleActiveScreenBuffer(g_hCurOut);
}


/*
 * Shut down and exit with status `r'
 * Careful: mch_windexit() may be called before mch_windinit()!
 */
	void
mch_windexit(
	int r)
{
	stoptermcap();
	outchar('\r');
	outchar('\n');
	flushbuf();

	if (g_fWindInitCalled)
		settmode(TMODE_COOK);

	if (g_hConOut != INVALID_HANDLE_VALUE)
	{
		(void) CloseHandle(g_hConOut);

		if (g_hSavOut != INVALID_HANDLE_VALUE)
		{
			SetConsoleTextAttribute(g_hSavOut, g_attrDefault);
			SetConsoleCursorInfo(g_hSavOut, &g_cci);
		}
	}

	ml_close_all(TRUE); 			/* remove all memfiles */

	if (g_fWindInitCalled)
	{
#ifdef WIN95_DEAD_KEYS_HACK
		keyboard_exit();
#endif

		mch_restore_title(3);

#ifdef MCH_WRITE_DUMP
		if (fdDump)
		{
			time_t t;

			time(&t);
			fputs(ctime(&t), fdDump);
			fclose(fdDump);
		}
		fdDump = NULL;
#endif /* MCH_WRITE_DUMP */
	}

	exit(r);
}


/*
 * Do we have an interactive window?
 */
	int
mch_check_win(
	int argc,
	char **argv)
{
	char temp [ 256 ];
	char * p;

	GetModuleFileName( NULL, temp, 255 );
	p = strrchr(temp, '.');
	if (p != NULL)
		*p = '\0';
	exe_name = strdup(temp);	/* remember the name of the executable */

	if (isatty(1))
		return OK;
	return FAIL;
}


/*
 * Return TRUE if the input comes from a terminal, FALSE otherwise.
 */
	int
mch_input_isatty()
{
	if (isatty(read_cmd_fd))
		return TRUE;
	return FALSE;
}


/*
 * Turn a filename into its canonical form.  Replace slashes with backslashes.
 * This used to replace backslashes with slashes, but that caused problems
 * when using the file name as a command.  We can't have a mix of slashes and
 * backslashes, because comparing file names will not work correctly.  The
 * commands that use file names should be prepared to handle the backslashes.
 */
	static void
canonicalize_filename(
	char* pszName)
{
	if (pszName == NULL)
		return;

	for ( ; *pszName;  pszName++)
	{
		if (*pszName == '/')
			*pszName = '\\';
#ifdef DOWNCASE_FILENAMES
		else
			*pszName = TO_LOWER(*pszName);
#endif /* DOWNCASE_FILENAMES */
	}
}


/*
 * fname_case(): Set the case of the filename, if it already exists.
 */
	void
fname_case(
	char_u *name)
{
#ifdef DOWNCASE_FILENAMES
	canonicalize_filename(name);
#else /* !DOWNCASE_FILENAMES */
	char szTrueName[_MAX_PATH + 1];
	char *psz, *pszPrev;
	const int len = (name != NULL)  ?  STRLEN(name)  :  0;

	if (len == 0)
		return;

	STRCPY(szTrueName, name);
	STRCAT(szTrueName, "\\");	/* sentinel */

	for (psz = szTrueName;  *psz != NUL;  psz++)
		if (*psz == '/')
			*psz = '\\';

	psz = pszPrev = szTrueName;

	/* Skip leading \\ in UNC name or drive letter */
	if (len > 2   &&  ((psz[0] == '\\'  &&  psz[1] == '\\')
					   || (isalpha(psz[0])  &&  psz[1] == ':')))
	{
		psz = pszPrev = szTrueName + 2;
	}

	while (*psz != NUL)
	{
		WIN32_FIND_DATA	fb;
		HANDLE		 	hFind;

		while (*psz != '\\')
			psz++;
		*psz = NUL;

		if ((hFind = FindFirstFile(szTrueName, &fb)) != INVALID_HANDLE_VALUE)
		{
			/* avoid ".." and ".", etc */
			if (_stricoll(pszPrev, fb.cFileName) == 0)
				STRCPY(pszPrev, fb.cFileName);
			FindClose(hFind);
		}

		*psz++ = '\\';
		pszPrev = psz;
	}

	*--psz = NUL;	/* remove sentinel */

	STRCPY(name, szTrueName);
#endif /* !DOWNCASE_FILENAMES */
}


/*
 * mch_settitle(): set titlebar of our window
 * Can the icon also be set?
 */
	void
mch_settitle(
	char_u *title,
	char_u *icon)
{
	if (title != NULL)
#ifdef USE_GUI_WIN32
		gui_mch_settitle(title, icon);
#else
		SetConsoleTitle(title);
#endif
}


/*
 * Restore the window/icon title.
 * which is one of:
 *	1: Just restore title
 *  2: Just restore icon (which we don't have)
 *	3: Restore title and icon (which we don't have)
 */
	void
mch_restore_title(
	int which)
{
	mch_settitle((which & 1) ? g_szOrigTitle : NULL, NULL);
}


/*
 * Return TRUE if we can restore the title (we can)
 */
	int
mch_can_restore_title()
{
	return TRUE;
}


/*
 * Return TRUE if we can restore the icon (we can't)
 */
	int
mch_can_restore_icon()
{
	return FALSE;
}


/*
 * 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)
{
	char szHostName[MAX_COMPUTERNAME_LENGTH + 1];
	DWORD cch = sizeof szHostName;

	if (GetComputerName(szHostName, &cch))
	{
		STRCPY(s, "PC ");
		STRNCPY(s + 3, szHostName, len - 3);
	}
	else
		STRNCPY(s, "PC (Win32 Vim)", len);
}


/*
 * 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)
{
	return (getcwd(buf, len) != NULL ? OK : FAIL);
}


/*
 * Get absolute filename into buffer 'buf' of length 'len' bytes,
 * turning all '/'s into '\\'s and getting the correct case of each
 * component of the filename.  Return OK or FAIL.
 */
	int
mch_FullName(
	char_u *fname,
	char_u *buf,
	int len,
	int force)
{
	int nResult = FAIL;

	if (fname == NULL)			/* always fail */
		return FAIL;

	if (_fullpath(buf, fname, len) == NULL)
	{
		/* failed, use the relative path name */
		STRNCPY(buf, fname, len);
	}
	else
		nResult = OK;

	fname_case(buf);

	return nResult;
}


/*
 * return TRUE if `fname' is an absolute path name
 */
	int
mch_isFullName(
	char_u *fname)
{
	char szName[_MAX_PATH];

	mch_FullName(fname, szName, _MAX_PATH, FALSE);

#ifdef DOWNCASE_FILENAMES
	return _stricoll(fname, szName) == 0;
#else /* !DOWNCASE_FILENAMES */
	return strcoll(fname, szName) == 0;
#endif /* !DOWNCASE_FILENAMES */
}


/*
 * get file permissions for `name'
 * -1 : error
 * else FILE_ATTRIBUTE_* defined in winnt.h
 */
	long
getperm(
	char_u *name)
{
	return GetFileAttributes(name);
}


/*
 * set file permission for `name' to `perm'
 */
	int
setperm(
	char_u *name,
	long perm)
{
	perm |= FILE_ATTRIBUTE_ARCHIVE;		/* file has changed, set archive bit */
	return SetFileAttributes(name, perm) ? OK : FAIL;
}


/*
 * 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 = getperm(name);

	if (f == -1)
		return FALSE;				/* file does not exist at all */

	return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
}


/*
 * handler for ctrl-break, ctrl-c interrupts, and fatal events.
 */
	static BOOL WINAPI
handler_routine(
	DWORD dwCtrlType)
{
	switch (dwCtrlType)
	{
	case CTRL_C_EVENT:
		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);
		sprintf((char *)IObuff, "Vim: Caught %s event\n",
				(dwCtrlType == CTRL_CLOSE_EVENT ? "close"
				 : dwCtrlType == CTRL_LOGOFF_EVENT ? "logoff" : "shutdown"));

#ifdef DEBUG
		OutputDebugString(IObuff);
#endif /* DEBUG */

		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;

	GetConsoleMode(g_hConIn,  &cmodein);
	GetConsoleMode(g_hCurOut, &cmodeout);

	if (tmode == TMODE_RAW)
	{
		cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
					 ENABLE_ECHO_INPUT);
		cmodein |= (
#ifdef USE_MOUSE
			(g_fMouseActive ? ENABLE_MOUSE_INPUT : 0) |
#endif
			ENABLE_WINDOW_INPUT);

		SetConsoleMode(g_hConIn, cmodein);

		cmodeout &= ~(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
		SetConsoleMode(g_hCurOut, cmodeout);
		SetConsoleCtrlHandler(handler_routine, TRUE);
	}
	else /* cooked */
	{
		cmodein =  g_cmodein;
		cmodeout = g_cmodeout;
		SetConsoleMode(g_hConIn,  g_cmodein);
		SetConsoleMode(g_hCurOut, g_cmodeout);

		SetConsoleCtrlHandler(handler_routine, FALSE);
	}

#ifdef MCH_WRITE_DUMP
	if (fdDump)
	{
		fprintf(fdDump, "mch_settmode(%s, CurOut = %s, in = %x, out = %x)\n",
				tmode == TMODE_RAW ? "raw" :
									tmode == TMODE_COOK ? "cooked" : "normal",
				(g_hCurOut == g_hSavOut ? "Sav" : "Con"),
				cmodein, cmodeout);
		fflush(fdDump);
	}
#endif /* MCH_WRITE_DUMP */
}


/*
 * Get the size of the current window in `Rows' and `Columns'
 */
	int
mch_get_winsize()
{
	CONSOLE_SCREEN_BUFFER_INFO csbi;

	if (GetConsoleScreenBufferInfo(g_hCurOut, &csbi))
	{
		Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
		Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
	}
	else
	{
		Rows = 25;
		Columns = 80;
	}

	if (Columns < MIN_COLUMNS  ||  Rows < MIN_ROWS + 1)
	{
		/* these values are overwritten by termcap size or default */
		Rows = 25;
		Columns = 80;
	}

	check_winsize();
	set_scroll_region(0, 0, Columns - 1, Rows - 1);

	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;
	DWORD			dwErr = 0;

#ifdef MCH_WRITE_DUMP
	if (fdDump)
	{
		fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
		fflush(fdDump);
	}
#endif

	GetConsoleScreenBufferInfo(hConsole, &csbi);

	/* 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);

	/* define the new console buffer size */
	coordScreen.X = xSize;
	coordScreen.Y = ySize;

	if (!SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect))
	{
#ifdef MCH_WRITE_DUMP
		if (fdDump)
		{
			fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
					GetLastError());
			fflush(fdDump);
		}
#endif
	}

	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_winsize()
{
	COORD coordScreen = GetLargestConsoleWindowSize(g_hCurOut);

	/* Clamp Rows and Columns to reasonable values */
	if (Rows > coordScreen.Y)
		Rows = coordScreen.Y;
	if (Columns > coordScreen.X)
		Columns = coordScreen.X;

	ResizeConBufAndWindow(g_hCurOut, Columns, Rows);
	set_scroll_region(0, 0, Columns - 1, Rows - 1);
}


/*
 * We have no job control, so fake it by starting a new shell.
 */
	void
mch_suspend()
{
	suspend_shell();
}


/*
 * Either execute a command by calling the shell or start a new shell
 */
	int
mch_call_shell(
	char_u *cmd,
	int options)			/* SHELL_FILTER if called by do_filter() */
							/* SHELL_COOKED if term needs cooked mode */
							/* SHELL_EXPAND if called by ExpandWildCards() */
{
	int		x;
	int		stopped_termcap_mode = FALSE;

	flushbuf();

#ifndef USE_GUI_WIN32
	/*
	 * ALWAYS switch to non-termcap mode, otherwise ":r !ls" may crash.
	 */
	if (g_hCurOut == g_hConOut)
	{
		termcap_mode_end();
		stopped_termcap_mode = TRUE;
	}
#endif /* USE_GUI_WIN32 */

#ifdef MCH_WRITE_DUMP
	if (fdDump)
	{
		fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
		fflush(fdDump);
	}
#endif

	signal(SIGINT, SIG_IGN);	/* we don't want to be killed here by Ctrl-C*/
	signal(SIGBREAK, SIG_IGN);	/* Nor by Ctrl-Break */

	if (options & SHELL_COOKED)
		settmode(TMODE_COOK);	/* set to normal mode */

	if (cmd == NULL)
	{
		x = system(p_sh);
	}
	else
	{
		/* we use "command" or "cmd" to start the shell; slow but easy */
		char newcmd[CMDBUFFSIZE + 100];

		sprintf(newcmd, "%s %s %s", p_sh, p_shcf, cmd);
		x = system(newcmd);
	}

	settmode(TMODE_RAW);			/* set to raw mode */

	if (x && !expand_interactively)
	{
		smsg("%d returned", x);
		msg_putchar('\n');
	}
	resettitle();

	signal(SIGINT, SIG_DFL);
	signal(SIGBREAK, SIG_DFL);

	if (stopped_termcap_mode)
		termcap_mode_start();

	return (x ? FAIL : OK);
}


#define FL_CHUNK 32

typedef struct filelist {
	char_u** file;
	int		nfiles;
	int		maxfiles;
} FileList;


/*
 * Add filename `f' to the list of files in `fl'
 */
	static void
addfile(
	FileList *fl,
	char *f,
	int isdir)
{
	char		*p, *pp;

	if (!fl->file)
	{
		fl->file = (char **) alloc(sizeof(char *) * FL_CHUNK);
		if (!fl->file)
			return;
		fl->nfiles = 0;
		fl->maxfiles = FL_CHUNK;
	}
	if (fl->nfiles >= fl->maxfiles)
	{
		char		**t;
		int			i;

		t = (char **) lalloc(sizeof(char *) * (fl->maxfiles + FL_CHUNK), TRUE);
		if (!t)
			return;
		for (i = fl->nfiles - 1; i >= 0; i--)
			t[i] = fl->file[i];
		vim_free(fl->file);
		fl->file = t;
		fl->maxfiles += FL_CHUNK;
	}
	p = alloc((unsigned) (strlen(f) + 1 + isdir));
	if (p)
	{
		/* replace slashes with backslashes while copying */
		for (pp = p; *f; ++f, ++pp)
		{
			if (*f == '/')
				*pp = '\\';
			else
				*pp = *f;
		}
		if (isdir)
			*pp++ = '\\';
		*pp = NUL;
	}
	fl->file[fl->nfiles++] = p;
}


/*
 * Does `s' contain a wildcard?
 */
	int
mch_has_wildcard(
	char_u *s)
{
	for ( ;  *s;  ++s)
		if (*s == '?' || *s == '*' || *s == '$')
			return TRUE;
	return FALSE;
}


/*
 * Copy a string, forcing it to lowercase if DOWNCASE_FILENAMES is defined
 */
#ifdef DOWNCASE_FILENAMES
	static void
strlowcpy(
	char *d,
	char *s)
{
	while (*s)
		*d++ = TO_LOWER(*s++);
	*d = NUL;
}
#else /* !DOWNCASE_FILENAMES */
# define strlowcpy(d, s) STRCPY(d, s)
#endif /* !DOWNCASE_FILENAMES */


/*
 * comparison function for qsort in expandpath
 */
	static int
pstrcmp(
	const void *a,
	const void *b)
{
	return (_stricoll(* (const char **) a, * (const char **) b));
}


/*
 * recursively build up a list of files in `fl' matching the first wildcard
 * in `path'.  `fonly' and `donly' are not used (files only and directories
 * only flags?).  If `notf' is set, we add `path' to `fl' even when no such
 * file exists.
 */
	static int
expandpath(
	FileList *fl,
	char *path,
	int fonly,
	int donly,
	int notf)
{
	char			buf[_MAX_PATH];
	char		   *p,
				   *s,
				   *e;
	int			 	lastn,
					c = 1,
					r;
	WIN32_FIND_DATA fb;
	HANDLE			hFind;
	int				found_one = FALSE;

	lastn = fl->nfiles;

	/*
	 * Find the first part in the path name that contains a wildcard.
	 * Copy it into `buf', including the preceding characters.
	 */
	p = buf;
	s = NULL;
	e = NULL;
	while (*path)
	{
		if (*path == '\\' || *path == ':' || *path == '/')
		{
			if (e)
				break;
			else
				s = p;
		}
		if (*path == '*' || *path == '?')
			e = p;
		*p++ = *path++;
	}
	e = p;
	if (s)
		s++;
	else
		s = buf;

	/* now we have one wildcard component between `s' and `e' */
	*e = NUL;
	r = 0;

	/* If we are expanding wildcards, we try both files and directories */
	if ((hFind = FindFirstFile(buf, &fb)) != INVALID_HANDLE_VALUE)
		while (c)
		{
			strlowcpy(s, fb.cFileName);

			/*
			 * Ignore "." and "..".
			 * When more follows, this must be a directory.
			 */
			if ((s[0] != '.'  ||
					(s[1] != NUL  &&  (s[1] != '.' || s[2] != NUL))) &&
					(*path == NUL || mch_isdir(buf)))
			{
				strcat(buf, path);
				if (!mch_has_wildcard(path))
					addfile(fl, buf, mch_isdir(buf));
				else
					r |= expandpath(fl, buf, fonly, donly, notf);
				found_one = TRUE;
			}

			c = FindNextFile(hFind, &fb);
		}

	if (!found_one)
	{
		/* not found */
		STRCPY(e, path);

		if (notf)
			addfile(fl, buf, FALSE);

		return 1;			   /* unexpanded or empty */
	}

	qsort(fl->file + lastn, fl->nfiles - lastn, sizeof(char *), pstrcmp);
	FindClose(hFind);

	return r;
}


/*
 * MSDOS rebuild of Scott Ballantyne's ExpandWildCards for amiga/arp. -- jw
 *
 * return OK for success, FAIL for error (you may lose some memory) and
 *	   put an error message in *file.
 *
 * `num_pat' is number of input patterns
 * `pat' is array of pointers to input patterns
 * `num_file' is pointer to number of matched file names
 * `file' is pointer to array of pointers to matched file names
 * On Unix/Win32, we do not check for `files_only' yet
 * `list_notfound' is ignored
 */
	int
ExpandWildCards(
	int num_pat,
	char_u **pat,
	int *num_file,
	char_u ***file,
	int files_only,
	int list_notfound)
{
	int				i,
					r = 0;
	FileList		f;
	char_u			*p;

	f.file = NULL;
	f.nfiles = 0;

	for (i = 0; i < num_pat; i++)
	{
		/*
		 * First expand environment variables.
		 */
		if (vim_strchr(pat[i], '$') != NULL)
		{
			p = alloc(MAXPATHL);
			if (p != NULL)
				expand_env(pat[i], p, MAXPATHL);
			else
				p = pat[i];
		}
		else
			p = pat[i];

		if (!mch_has_wildcard(p))
			addfile(&f, p, files_only ? FALSE : mch_isdir(p));
		else
			r |= expandpath(&f, p, files_only, 0, list_notfound);

		if (p != pat[i])
			vim_free(p);
	}

	*num_file = f.nfiles;
	*file = (*num_file > 0) ? f.file : (char_u **)"";

	return (*num_file > 0) ? OK : FAIL;
}


#ifdef vim_chdir
# undef vim_chdir
#endif

/*
 * The normal _chdir() does not change the default drive.  This one does.
 * Returning 0 implies success; -1 implies failure.
 */
	int
vim_chdir(
	char *path)
{
	if (path[0] == NUL)			/* just checking... */
		return -1;

	if (isalpha(path[0])  &&  path[1] == ':')	/* has a drive name */
	{
		if (_chdrive(TO_UPPER(path[0]) - 'A' + 1) != 0)
			return -1;			/* invalid drive name */
		path += 2;
	}

	if (*path == NUL)			/* drive name only */
		return 0;

	return chdir(path);		   /* let the normal chdir() do the rest */
}


/*
 * Copy the contents of screen buffer hSrc to the bottom-left corner
 * of screen buffer hDst.
 */
	static void
copy_screen_buffer_text(
	HANDLE hSrc,
	HANDLE hDst)
{
	int		i, j, nSrcWidth, nSrcHeight, nDstWidth, nDstHeight;
	COORD   coordOrigin;
	DWORD   dwDummy;
	LPSTR   pszOldText;
	CONSOLE_SCREEN_BUFFER_INFO csbiSrc, csbiDst;

#ifdef MCH_WRITE_DUMP
	if (fdDump)
	{
		fprintf(fdDump, "copy_screen_buffer_text(%s, %s)\n",
				(hSrc == g_hSavOut ? "Sav" : "Con"),
				(hDst == g_hSavOut ? "Sav" : "Con"));
		fflush(fdDump);
	}
#endif /* MCH_WRITE_DUMP */

	GetConsoleScreenBufferInfo(hSrc, &csbiSrc);
	nSrcWidth =  csbiSrc.srWindow.Right  - csbiSrc.srWindow.Left + 1;
	nSrcHeight = csbiSrc.srWindow.Bottom - csbiSrc.srWindow.Top  + 1;

	GetConsoleScreenBufferInfo(hDst, &csbiDst);
	nDstWidth =  csbiDst.srWindow.Right  - csbiDst.srWindow.Left + 1;
	nDstHeight = csbiDst.srWindow.Bottom - csbiDst.srWindow.Top  + 1;

	pszOldText = (LPSTR) alloc(nDstHeight * nDstWidth);

	/* clear the top few lines if Src shorter than Dst */
	for (i = 0;  i < nDstHeight - nSrcHeight;  i++)
	{
		for (j = 0;  j < nDstWidth;  j++)
			pszOldText[i * nDstWidth + j] = ' ';
	}

	/* Grab text off source screen. */
	coordOrigin.X = 0;
	coordOrigin.Y = (SHORT) max(0, csbiSrc.srWindow.Bottom + 1 - nDstHeight);

	for (i = 0;  i < min(nSrcHeight, nDstHeight);  i++)
	{
		LPSTR psz = pszOldText
					 + (i + max(0, nDstHeight - nSrcHeight)) * nDstWidth;

		ReadConsoleOutputCharacter(hSrc, psz, min(nDstWidth, nSrcWidth),
								   coordOrigin, &dwDummy);
		coordOrigin.Y++;

		/* clear the last few columns if Src narrower than Dst */
		for (j = nSrcWidth;  j < nDstWidth;  j++)
			psz[j] = ' ';
	}

	/* paste Src's text onto Dst */
	coordOrigin.Y = csbiDst.srWindow.Top;
	WriteConsoleOutputCharacter(hDst, pszOldText, nDstHeight * nDstWidth,
								coordOrigin, &dwDummy);
	vim_free(pszOldText);
}


/* keep track of state of original console window */
static SMALL_RECT	g_srOrigWindowRect;
static COORD		g_coordOrig;
static WORD			g_attrSave = 0;


/*
 * Start termcap mode by switching to our allocated screen buffer
 */
	static void
termcap_mode_start()
{
	CONSOLE_SCREEN_BUFFER_INFO csbi;
	DWORD dwDummy;
	COORD coord;
	WORD wAttr = (WORD) (g_attrSave ? g_attrSave : g_attrCurrent);

	GetConsoleScreenBufferInfo(g_hSavOut, &csbi);
	g_srOrigWindowRect = csbi.srWindow;

	g_coordOrig.X = 0;
	g_coordOrig.Y = csbi.dwCursorPosition.Y;

	if (g_hConOut == INVALID_HANDLE_VALUE)
	{
		/* Create a new screen buffer in which we do all of our editing.
		 * This means we can restore the original screen when we finish. */
		g_hConOut = CreateConsoleScreenBuffer(GENERIC_WRITE | GENERIC_READ,
											  0, (LPSECURITY_ATTRIBUTES) NULL,
											  CONSOLE_TEXTMODE_BUFFER,
											  (LPVOID) NULL);
	}

	coord.X = coord.Y = 0;
	FillConsoleOutputCharacter(g_hConOut, ' ', Rows * Columns, coord, &dwDummy);
	FillConsoleOutputAttribute(g_hConOut, wAttr, Rows*Columns, coord, &dwDummy);

	copy_screen_buffer_text(g_hSavOut, g_hConOut);

	g_hCurOut = g_hConOut;
	SetConsoleActiveScreenBuffer(g_hCurOut);

	ResizeConBufAndWindow(g_hCurOut, Columns, Rows);
	set_scroll_region(0, 0, Columns - 1, Rows - 1);
	check_winsize();

	resettitle();

	textattr(wAttr);
}


/*
 * Turn off termcap mode by restoring the screen buffer we had upon startup
 */
	static void
termcap_mode_end()
{
	g_attrSave = g_attrCurrent;

	ResizeConBufAndWindow(g_hCurOut, g_nOldColumns, g_nOldRows);
	check_winsize();

	g_hCurOut = g_hSavOut;
	SetConsoleActiveScreenBuffer(g_hCurOut);
	SetConsoleCursorInfo(g_hCurOut, &g_cci);

	normvideo();

	if (!p_rs)
		g_coordOrig.Y = g_srOrigWindowRect.Bottom;

	SetConsoleCursorPosition(g_hCurOut, g_coordOrig);

	if (!p_rs)
		copy_screen_buffer_text(g_hConOut, g_hSavOut);

	clear_chars(g_coordOrig,
				g_srOrigWindowRect.Right - g_srOrigWindowRect.Left + 1);

	mch_restore_title(3);

	SetConsoleMode(g_hConIn,  g_cmodein);
	SetConsoleMode(g_hSavOut, g_cmodeout);
}

/*
 * Switching off termcap mode is only allowed when Columns is 80, otherwise a
 * crash may result.  It's always allowed on NT.
 */
	int
can_end_termcap_mode(
	int give_msg)
{
	if (g_PlatformId == VER_PLATFORM_WIN32_NT  ||  Columns == 80)
		return TRUE;
	if (give_msg)
		msg("'columns' is not 80, cannot execute external commands");
	return FALSE;
}

/*
 * clear `n' chars, starting from `coord'
 */
	static void
clear_chars(
	COORD coord,
	DWORD n)
{
	DWORD dwDummy;

	FillConsoleOutputCharacter(g_hCurOut, ' ', n, coord, &dwDummy);
	FillConsoleOutputAttribute(g_hCurOut, g_attrCurrent, n, coord, &dwDummy);
}


/*
 * Clear the screen
 */
	static void
clear_screen()
{
	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()
{
	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()
{
	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_hCurOut, &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_hCurOut, &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)
{
	COORD coord2;

	if (x < 1  ||  x > (unsigned) Columns  ||  y < 1  ||  y > (unsigned) Rows)
		return;

	/* Should we check against g_srScrollRegion? */

	/* external cursor coords are 1-based; internal are 0-based */
	g_coord.X = coord2.X = x - 1;
	g_coord.Y = coord2.Y = y - 1;

	/* If we are using the window buffer that we had upon startup, make
	 * sure to position cursor relative to the window upon that buffer */
	if (g_hCurOut == g_hSavOut)
	{
		CONSOLE_SCREEN_BUFFER_INFO csbi;

		GetConsoleScreenBufferInfo(g_hCurOut, &csbi);
		g_srOrigWindowRect = csbi.srWindow;

		coord2.X += (SHORT) (g_srOrigWindowRect.Left);
		coord2.Y += (SHORT) (g_srOrigWindowRect.Bottom + 1 - Rows);
	}

	SetConsoleCursorPosition(g_hCurOut, coord2);
}


/*
 * Set the current text attribute = (foreground | background)
 *
 * COLOR		FOREGROUND	BACKGROUND
 *  black			0			0
 *  blue			1		   16
 *  green			2		   32
 *  cyan			3		   48
 *  red				4		   64
 *  magenta			5		   80
 *  brown			6		   96
 *  lightgray		7		  112
 *  darkgray		8		  128
 *  lightblue		9		  144
 *  lightgreen	   10		  160
 *  lightcyan	   11		  176
 *  lightred	   12		  192
 *  lightmagenta   13		  208
 *  yellow		   14		  224
 *  white		   15		  240
 */
	static void
textattr(
	WORD wAttr)
{
	g_attrCurrent = wAttr;

	SetConsoleTextAttribute(g_hCurOut, wAttr);
}


	static void
textcolor(
	WORD wAttr)
{
	g_attrCurrent = (g_attrCurrent & 0xf0) + wAttr;

	SetConsoleTextAttribute(g_hCurOut, g_attrCurrent);
}


	static void
textbackground(
	WORD wAttr)
{
	g_attrCurrent = (g_attrCurrent & 0x0f) + (wAttr << 4);

	SetConsoleTextAttribute(g_hCurOut, 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()
{
	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;
	}
}


/*
 * 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));

	ReadConsoleOutputAttribute(g_hCurOut, oldattrs, Rows * Columns,
							   coordOrigin, &dwDummy);
	FillConsoleOutputAttribute(g_hCurOut, attrFlash, Rows * Columns,
							   coordOrigin, &dwDummy);

	Sleep(15);		/* wait for 15 msec */
	WriteConsoleOutputAttribute(g_hCurOut, oldattrs, Rows * Columns,
	 							coordOrigin, &dwDummy);
	vim_free(oldattrs);
}


/*
 * Make the cursor visible or invisible
 */
	static void
cursor_visible(
	BOOL fVisible)
{
	CONSOLE_CURSOR_INFO cci;

	cci.bVisible = fVisible;
	/* make cursor big and visible (100 on Win95 makes it disappear)  */
	cci.dwSize = 99;		   /* 100 percent cursor */
	SetConsoleCursorInfo(g_hCurOut, &cci);
}


/*
 * write `cchToWrite' characters in `pchBuf' to the screen
 */
	static BOOL
write_chars(
	LPCSTR pchBuf,
	DWORD  cchToWrite,
	DWORD* pcchWritten)
{
	BOOL f;
	COORD coord2 = g_coord;

	if (g_hCurOut == g_hSavOut)
	{
		CONSOLE_SCREEN_BUFFER_INFO csbi;

		GetConsoleScreenBufferInfo(g_hCurOut, &csbi);

		coord2.X += (SHORT) (csbi.srWindow.Left);
		coord2.Y += (SHORT) (csbi.srWindow.Bottom + 1 - Rows);
	}

	FillConsoleOutputAttribute(g_hCurOut, g_attrCurrent, cchToWrite,
							   coord2, pcchWritten);
	f = WriteConsoleOutputCharacter(g_hCurOut, pchBuf, cchToWrite,
									coord2, pcchWritten);

	g_coord.X += (SHORT) *pcchWritten;

	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 f;
}


/*
 * 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)
				prefix = 1;
		}

		if (prefix)
		{
			DWORD nWritten;

			if (write_chars(s, prefix, &nWritten))
			{
#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;

			if (write_chars(s, 1, &nWritten))
			{
#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
}


/*
 * Delay for half a second.
 */
	void
mch_delay(
	long	msec,
	int		ignoreinput)
{
	if (ignoreinput)
		Sleep((int)msec);
	else
		WaitForChar(msec);
}


#ifdef vim_remove
# undef vim_remove
#endif

/*
 * this version of remove is not scared by a readonly (backup) file
 */
	int
vim_remove(
	char_u *name)
{
	SetFileAttributes(name, FILE_ATTRIBUTE_NORMAL);
	return DeleteFile(name) ? 0 : -1;
}


/*
 * check for an "interrupt signal": CTRL-break or CTRL-C
 */
	void
mch_breakcheck()
{
	if (g_fCtrlCPressed || g_fCBrkPressed)
	{
		g_fCtrlCPressed = g_fCBrkPressed = FALSE;
		got_int = TRUE;
	}
}


/*
 * How much memory is available?
 */
	long
mch_avail_mem(
	int special)
{
	return LONG_MAX;		/* virtual memory, eh? */
}


/*
 * return non-zero if a character is available
 */
	int
mch_char_avail()
{
	return WaitForChar(0L);
}


/*
 * set screen mode, always fails.
 */
	int
mch_screenmode(
	char_u *arg)
{
	EMSG("Screen mode setting not supported");
	return FAIL;
}


/*
 * win95rename works around a bug in rename (aka MoveFile) in
 * Windows 95: rename("foo.bar", "foo.bar~") will generate a
 * file whose shortfilename is "FOO.BAR" (its longfilename 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".
 *
 * Like rename(), returns 0 upon success, non-zero upon failure.
 * Should probably set errno appropriately when errors occur.
 */
	int
win95rename(
	const char* pszOldFile,
	const char* pszNewFile)
{
	char szTempFile[_MAX_PATH];
	char szNewPath[_MAX_PATH];
	char* pszFilePart;
	HANDLE hf;

#undef rename

	/* get base path of new filename */
	if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0)
		return -1;
	else
		*pszFilePart = NUL;

	/* Get (and create) a unique temporary filename 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))
		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 */
}

/*
 * Special version of getenv(): use $HOME when $VIM not defined.
 */
	char_u *
vim_getenv(
	char_u *var)
{
	char_u	*retval;
	static char *exepath = NULL;
	char temp [ 256 ];
	char *p;

	retval = (char_u *)getenv((char *)var);

	if (retval == NULL && STRCMP(var, "VIM") == 0)
		retval = (char_u *)getenv("HOME");
#if 0		/* this is already done in expand_env(), using exe_name. */
// ron's tweak:
	if (retval == NULL && STRCMP(var, "VIM") == 0)
	{
		// get exe name
		if (exepath == NULL)
		{
			GetModuleFileName(NULL, temp, 255);
			// find last \\ and truncate there:
			p = strrchr(temp, '\\');
			if (p != NULL)
			{
				*p = '\0';
			}
			exepath = strdup(temp);
		}
		retval = (char_u *) exepath;
	}
#endif

#ifdef MCH_WRITE_DUMP
	if (fdDump)
	{
		fprintf(fdDump, "$%s = \"%s\"\n", var, retval);
		fflush(fdDump);
	}
#endif

	return retval;
}


/*
 * 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;
}


#ifdef USE_CLIPBOARD
/*
 * Clipboard stuff, for cutting and pasting text to other windows.
 */

/*
 * Get the current selection and put it in the clipboard register.
 */
	void
clip_mch_request_selection()
{
	int		type;
	HGLOBAL	hMem;
	char_u	*str = NULL;

	if (OpenClipboard(GetActiveWindow()))
	{
		/* Check for vim's own clipboard format first */
		if ((hMem = GetClipboardData(clipboard.format)) != NULL)
		{
			str = (char_u *)hMem;
			switch (*str++)
			{
				default:
				case 'L':		type = MLINE;	break;
				case 'C':		type = MCHAR;	break;
				case 'B':		type = MBLOCK;	break;
			}
			/* TRACE("Got '%c' type\n", str[-1]); */
		}
		/* Otherwise, check for the normal text format */
		else if ((hMem = GetClipboardData(CF_TEXT)) != NULL)
		{
			str = (char_u *)hMem;
			type = (strchr((char*) str, '\r') != NULL) ? MLINE : MCHAR;
			/* TRACE("TEXT\n"); */
		}

		if (hMem != NULL && *str != NUL)
		{
			LPCSTR psz = (LPCSTR) str;
			const int nLen = STRLEN(psz) + 1;
			char_u *temp_clipboard = (char_u *)lalloc(STRLEN(psz) + 1, TRUE);
			char_u *pszTemp = temp_clipboard;

			if (temp_clipboard != NULL)
			{
				while (*psz != NUL)
				{
					const char* pszNL = strchr(psz, '\r');
					const int len = (pszNL != NULL) ? pszNL - psz : STRLEN(psz);

					STRNCPY(pszTemp, psz, len);

					pszTemp += len;
					if (pszNL != NULL)
						*pszTemp++ = '\n';

					psz += len + ((pszNL != NULL) ? 2 : 0);
				}

				*pszTemp = NUL;
				clip_yank_selection(type, temp_clipboard,
									pszTemp - temp_clipboard);
				vim_free(temp_clipboard);
			}
		}
		CloseClipboard();
	}
}

/*
 * Make vim the owner of the current selection.
 */
	void
clip_mch_lose_selection()
{
	/* Nothing needs to be done here */
}

/*
 * Send the current selection to the clipboard.  Must own the selection first.
 */
	static void
clip_w32_set_clipboard()
{
	char_u	*str = NULL;
	long_u  cch;
	int		type;
	HGLOBAL	hMem = NULL;
	HGLOBAL	hMemVim = NULL;
	LPSTR	lpszMem = NULL;
	LPSTR	lpszMemVim = NULL;

	if (!clipboard.owned)
		return;

	clip_get_selection();

	/*
	 * Once we set the clipboard, lose ownership.  If another application sets
	 * the clipboard, we don't want to think that we still own it.
	 */
	clipboard.owned = FALSE;

	type = clip_convert_selection(&str, &cch);

	if (type < 0)
		return;
	
	if ((hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cch+1)) != NULL
		&&  (lpszMem = (LPSTR) GlobalLock(hMem)) != NULL
		&&  (hMemVim = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, cch+2)) != NULL
		&&  (lpszMemVim = (LPSTR) GlobalLock(hMemVim)) != NULL)
	{
		switch (type)
		{
			default:
			case MLINE:		*lpszMemVim++ = 'L';	break;
			case MCHAR:		*lpszMemVim++ = 'C';	break;
			case MBLOCK:	*lpszMemVim++ = 'B';	break;
		}
		
		STRNCPY(lpszMem, str, cch);
		lpszMem[cch] = NUL;
		
		STRNCPY(lpszMemVim, str, cch);
		lpszMemVim[cch] = NUL;

		GlobalUnlock(hMem);
		GlobalUnlock(hMemVim);

		if (OpenClipboard(GetActiveWindow()))
		{
			if (EmptyClipboard()
					&& SetClipboardData(clipboard.format, hMemVim) != NULL
					&& SetClipboardData(CF_TEXT, hMem) != NULL)

			CloseClipboard();
		}
	}

	vim_free(str);
}

/*
 * Make vim the owner of the current selection.  Return OK upon success.
 */
	int
clip_mch_own_selection()
{
	clip_w32_set_clipboard();
	return OK;
}

/*
 * We assume that another application will neither get nor set the clipboard
 * contents while we have the focus.  That way we only have to send data to the
 * clipboard when we lose focus. -- webb.
 */
	void
clip_mch_lose_focus()
{
	/*
	 * Do nothing.  Now we don't wait till we lose focus.  We just set the
	 * clipboard immediately whenever appropriate. -- webb
	 */
	/* clip_w32_set_clipboard(); */
}
#endif /* USE_CLIPBOARD */


/*
 * Debugging helper: expose the MCH_WRITE_DUMP stuff to other modules
 */
	void
DumpPutS(
	const char* psz)
{
# ifdef MCH_WRITE_DUMP
	if (fdDump)
	{
		fputs(psz, fdDump);
		if (psz[strlen(psz) - 1] != '\n')
			fputc('\n', fdDump);
		fflush(fdDump);
	}
# endif /* MCH_WRITE_DUMP */
}

#ifdef _DEBUG

void __cdecl
Trace(
	char *pszFormat,
	...)
{
	CHAR szBuff[2048];
	va_list args;

	va_start(args, pszFormat);
	vsprintf(szBuff, pszFormat, args);
	va_end(args);

	OutputDebugString(szBuff);
}

#endif //_DEBUG

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.