xref: /vim-8.2.3635/src/os_win32.c (revision 15d0a8c7)
1 /* vi:set ts=8 sts=4 sw=4:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 /*
10  * os_win32.c
11  *
12  * Used for both the console version and the Win32 GUI.  A lot of code is for
13  * the console version only, so there is a lot of "#ifndef FEAT_GUI_W32".
14  *
15  * Win32 (Windows NT and Windows 95) system-dependent routines.
16  * Portions lifted from the Win32 SDK samples, the MSDOS-dependent code,
17  * NetHack 3.1.3, GNU Emacs 19.30, and Vile 5.5.
18  *
19  * George V. Reilly <[email protected]> wrote most of this.
20  * Roger Knobbe <[email protected]> did the initial port of Vim 3.0.
21  */
22 
23 #include <io.h>
24 #include "vim.h"
25 
26 #ifdef FEAT_MZSCHEME
27 # include "if_mzsch.h"
28 #endif
29 
30 #ifdef HAVE_FCNTL_H
31 # include <fcntl.h>
32 #endif
33 #include <sys/types.h>
34 #include <errno.h>
35 #include <signal.h>
36 #include <limits.h>
37 #include <process.h>
38 
39 #undef chdir
40 #ifdef __GNUC__
41 # ifndef __MINGW32__
42 #  include <dirent.h>
43 # endif
44 #else
45 # include <direct.h>
46 #endif
47 
48 #if defined(FEAT_TITLE) && !defined(FEAT_GUI_W32)
49 # include <shellapi.h>
50 #endif
51 
52 #ifdef __MINGW32__
53 # ifndef FROM_LEFT_1ST_BUTTON_PRESSED
54 #  define FROM_LEFT_1ST_BUTTON_PRESSED    0x0001
55 # endif
56 # ifndef RIGHTMOST_BUTTON_PRESSED
57 #  define RIGHTMOST_BUTTON_PRESSED	  0x0002
58 # endif
59 # ifndef FROM_LEFT_2ND_BUTTON_PRESSED
60 #  define FROM_LEFT_2ND_BUTTON_PRESSED    0x0004
61 # endif
62 # ifndef FROM_LEFT_3RD_BUTTON_PRESSED
63 #  define FROM_LEFT_3RD_BUTTON_PRESSED    0x0008
64 # endif
65 # ifndef FROM_LEFT_4TH_BUTTON_PRESSED
66 #  define FROM_LEFT_4TH_BUTTON_PRESSED    0x0010
67 # endif
68 
69 /*
70  * EventFlags
71  */
72 # ifndef MOUSE_MOVED
73 #  define MOUSE_MOVED   0x0001
74 # endif
75 # ifndef DOUBLE_CLICK
76 #  define DOUBLE_CLICK  0x0002
77 # endif
78 #endif
79 
80 /* Record all output and all keyboard & mouse input */
81 /* #define MCH_WRITE_DUMP */
82 
83 #ifdef MCH_WRITE_DUMP
84 FILE* fdDump = NULL;
85 #endif
86 
87 /*
88  * When generating prototypes for Win32 on Unix, these lines make the syntax
89  * errors disappear.  They do not need to be correct.
90  */
91 #ifdef PROTO
92 #define WINAPI
93 #define WINBASEAPI
94 typedef char * LPCSTR;
95 typedef int ACCESS_MASK;
96 typedef int BOOL;
97 typedef int COLORREF;
98 typedef int CONSOLE_CURSOR_INFO;
99 typedef int COORD;
100 typedef int DWORD;
101 typedef int HANDLE;
102 typedef int HDC;
103 typedef int HFONT;
104 typedef int HICON;
105 typedef int HINSTANCE;
106 typedef int HWND;
107 typedef int INPUT_RECORD;
108 typedef int KEY_EVENT_RECORD;
109 typedef int LOGFONT;
110 typedef int LPBOOL;
111 typedef int LPCTSTR;
112 typedef int LPDWORD;
113 typedef int LPSTR;
114 typedef int LPTSTR;
115 typedef int LPVOID;
116 typedef int MOUSE_EVENT_RECORD;
117 typedef int PACL;
118 typedef int PDWORD;
119 typedef int PHANDLE;
120 typedef int PRINTDLG;
121 typedef int PSECURITY_DESCRIPTOR;
122 typedef int PSID;
123 typedef int SECURITY_INFORMATION;
124 typedef int SHORT;
125 typedef int SMALL_RECT;
126 typedef int TEXTMETRIC;
127 typedef int TOKEN_INFORMATION_CLASS;
128 typedef int TRUSTEE;
129 typedef int WORD;
130 typedef int WCHAR;
131 typedef void VOID;
132 #endif
133 
134 #ifndef FEAT_GUI_W32
135 /* Undocumented API in kernel32.dll needed to work around dead key bug in
136  * console-mode applications in NT 4.0.  If you switch keyboard layouts
137  * in a console app to a layout that includes dead keys and then hit a
138  * dead key, a call to ToAscii will trash the stack.  My thanks to Ian James
139  * and Michael Dietrich for helping me figure out this workaround.
140  */
141 
142 /* WINBASEAPI BOOL WINAPI GetConsoleKeyboardLayoutNameA(LPSTR); */
143 #ifndef WINBASEAPI
144 # define WINBASEAPI __stdcall
145 #endif
146 #if defined(__BORLANDC__)
147 typedef BOOL (__stdcall *PFNGCKLN)(LPSTR);
148 #else
149 typedef WINBASEAPI BOOL (WINAPI *PFNGCKLN)(LPSTR);
150 #endif
151 PFNGCKLN    s_pfnGetConsoleKeyboardLayoutName = NULL;
152 #endif
153 
154 #if defined(__BORLANDC__)
155 /* Strangely Borland uses a non-standard name. */
156 # define wcsicmp(a, b) wcscmpi((a), (b))
157 #endif
158 
159 #ifndef FEAT_GUI_W32
160 /* Win32 Console handles for input and output */
161 static HANDLE g_hConIn  = INVALID_HANDLE_VALUE;
162 static HANDLE g_hConOut = INVALID_HANDLE_VALUE;
163 
164 /* Win32 Screen buffer,coordinate,console I/O information */
165 static SMALL_RECT g_srScrollRegion;
166 static COORD	  g_coord;  /* 0-based, but external coords are 1-based */
167 
168 /* The attribute of the screen when the editor was started */
169 static WORD  g_attrDefault = 7;  /* lightgray text on black background */
170 static WORD  g_attrCurrent;
171 
172 static int g_fCBrkPressed = FALSE;  /* set by ctrl-break interrupt */
173 static int g_fCtrlCPressed = FALSE; /* set when ctrl-C or ctrl-break detected */
174 static int g_fForceExit = FALSE;    /* set when forcefully exiting */
175 
176 static void termcap_mode_start(void);
177 static void termcap_mode_end(void);
178 static void clear_chars(COORD coord, DWORD n);
179 static void clear_screen(void);
180 static void clear_to_end_of_display(void);
181 static void clear_to_end_of_line(void);
182 static void scroll(unsigned cLines);
183 static void set_scroll_region(unsigned left, unsigned top,
184 			      unsigned right, unsigned bottom);
185 static void insert_lines(unsigned cLines);
186 static void delete_lines(unsigned cLines);
187 static void gotoxy(unsigned x, unsigned y);
188 static void normvideo(void);
189 static void textattr(WORD wAttr);
190 static void textcolor(WORD wAttr);
191 static void textbackground(WORD wAttr);
192 static void standout(void);
193 static void standend(void);
194 static void visual_bell(void);
195 static void cursor_visible(BOOL fVisible);
196 static BOOL write_chars(LPCSTR pchBuf, DWORD cchToWrite);
197 static char_u tgetch(int *pmodifiers, char_u *pch2);
198 static void create_conin(void);
199 static int s_cursor_visible = TRUE;
200 static int did_create_conin = FALSE;
201 #else
202 static int s_dont_use_vimrun = TRUE;
203 static int need_vimrun_warning = FALSE;
204 static char *vimrun_path = "vimrun ";
205 #endif
206 
207 #ifndef FEAT_GUI_W32
208 static int suppress_winsize = 1;	/* don't fiddle with console */
209 #endif
210 
211     static void
212 get_exe_name(void)
213 {
214     char	temp[256];
215 
216     if (exe_name == NULL)
217     {
218 	/* store the name of the executable, may be used for $VIM */
219 	GetModuleFileName(NULL, temp, 255);
220 	if (*temp != NUL)
221 	    exe_name = FullName_save((char_u *)temp, FALSE);
222     }
223 }
224 
225 #if defined(DYNAMIC_GETTEXT) || defined(PROTO)
226 # ifndef GETTEXT_DLL
227 #  define GETTEXT_DLL "libintl.dll"
228 # endif
229 /* Dummy funcitons */
230 static char* null_libintl_gettext(const char *);
231 static char* null_libintl_textdomain(const char *);
232 static char* null_libintl_bindtextdomain(const char *, const char *);
233 
234 static HINSTANCE hLibintlDLL = 0;
235 char* (*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
236 char* (*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
237 char* (*dyn_libintl_bindtextdomain)(const char *, const char *)
238 						= null_libintl_bindtextdomain;
239 
240     int
241 dyn_libintl_init(char *libname)
242 {
243     int i;
244     static struct
245     {
246 	char	    *name;
247 	FARPROC	    *ptr;
248     } libintl_entry[] =
249     {
250 	{"gettext", (FARPROC*)&dyn_libintl_gettext},
251 	{"textdomain", (FARPROC*)&dyn_libintl_textdomain},
252 	{"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
253 	{NULL, NULL}
254     };
255 
256     /* No need to initialize twice. */
257     if (hLibintlDLL)
258 	return 1;
259     /* Load gettext library (libintl.dll) */
260     hLibintlDLL = LoadLibrary(libname != NULL ? libname : GETTEXT_DLL);
261     if (!hLibintlDLL)
262     {
263 	char_u	    dirname[_MAX_PATH];
264 
265 	/* Try using the path from gvim.exe to find the .dll there. */
266 	get_exe_name();
267 	STRCPY(dirname, exe_name);
268 	STRCPY(gettail(dirname), GETTEXT_DLL);
269 	hLibintlDLL = LoadLibrary((char *)dirname);
270 	if (!hLibintlDLL)
271 	{
272 	    if (p_verbose > 0)
273 		EMSG2(_(e_loadlib), GETTEXT_DLL);
274 	    return 0;
275 	}
276     }
277     for (i = 0; libintl_entry[i].name != NULL
278 					 && libintl_entry[i].ptr != NULL; ++i)
279     {
280 	if ((*libintl_entry[i].ptr = (FARPROC)GetProcAddress(hLibintlDLL,
281 					      libintl_entry[i].name)) == NULL)
282 	{
283 	    dyn_libintl_end();
284 	    if (p_verbose > 0)
285 		EMSG2(_(e_loadfunc), libintl_entry[i].name);
286 	    return 0;
287 	}
288     }
289     return 1;
290 }
291 
292     void
293 dyn_libintl_end()
294 {
295     if (hLibintlDLL)
296 	FreeLibrary(hLibintlDLL);
297     hLibintlDLL			= NULL;
298     dyn_libintl_gettext		= null_libintl_gettext;
299     dyn_libintl_textdomain	= null_libintl_textdomain;
300     dyn_libintl_bindtextdomain	= null_libintl_bindtextdomain;
301 }
302 
303     static char *
304 null_libintl_gettext(const char* msgid)
305 {
306     return (char*)msgid;
307 }
308 
309     static char *
310 null_libintl_bindtextdomain(const char* domainname, const char* dirname)
311 {
312     return NULL;
313 }
314 
315     static char *
316 null_libintl_textdomain(const char* domainname)
317 {
318     return NULL;
319 }
320 
321 #endif /* DYNAMIC_GETTEXT */
322 
323 /* This symbol is not defined in older versions of the SDK or Visual C++ */
324 
325 #ifndef VER_PLATFORM_WIN32_WINDOWS
326 # define VER_PLATFORM_WIN32_WINDOWS 1
327 #endif
328 
329 DWORD g_PlatformId;
330 
331 #ifdef HAVE_ACL
332 # include <aclapi.h>
333 /*
334  * These are needed to dynamically load the ADVAPI DLL, which is not
335  * implemented under Windows 95 (and causes VIM to crash)
336  */
337 typedef DWORD (WINAPI *PSNSECINFO) (LPTSTR, enum SE_OBJECT_TYPE,
338 	SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
339 typedef DWORD (WINAPI *PGNSECINFO) (LPSTR, enum SE_OBJECT_TYPE,
340 	SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *,
341 	PSECURITY_DESCRIPTOR *);
342 
343 static HANDLE advapi_lib = NULL;	/* Handle for ADVAPI library */
344 static PSNSECINFO pSetNamedSecurityInfo;
345 static PGNSECINFO pGetNamedSecurityInfo;
346 #endif
347 
348 /*
349  * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
350  * VER_PLATFORM_WIN32_WINDOWS (Win95).
351  */
352     void
353 PlatformId(void)
354 {
355     static int done = FALSE;
356 
357     if (!done)
358     {
359 	OSVERSIONINFO ovi;
360 
361 	ovi.dwOSVersionInfoSize = sizeof(ovi);
362 	GetVersionEx(&ovi);
363 
364 	g_PlatformId = ovi.dwPlatformId;
365 
366 #ifdef HAVE_ACL
367 	/*
368 	 * Load the ADVAPI runtime if we are on anything
369 	 * other than Windows 95
370 	 */
371 	if (g_PlatformId == VER_PLATFORM_WIN32_NT)
372 	{
373 	    /*
374 	     * do this load.  Problems: Doesn't unload at end of run (this is
375 	     * theoretically okay, since Windows should unload it when VIM
376 	     * terminates).  Should we be using the 'mch_libcall' routines?
377 	     * Seems like a lot of overhead to load/unload ADVAPI32.DLL each
378 	     * time we verify security...
379 	     */
380 	    advapi_lib = LoadLibrary("ADVAPI32.DLL");
381 	    if (advapi_lib != NULL)
382 	    {
383 		pSetNamedSecurityInfo = (PSNSECINFO)GetProcAddress(advapi_lib,
384 						      "SetNamedSecurityInfoA");
385 		pGetNamedSecurityInfo = (PGNSECINFO)GetProcAddress(advapi_lib,
386 						      "GetNamedSecurityInfoA");
387 		if (pSetNamedSecurityInfo == NULL
388 			|| pGetNamedSecurityInfo == NULL)
389 		{
390 		    /* If we can't get the function addresses, set advapi_lib
391 		     * to NULL so that we don't use them. */
392 		    FreeLibrary(advapi_lib);
393 		    advapi_lib = NULL;
394 		}
395 	    }
396 	}
397 #endif
398 	done = TRUE;
399     }
400 }
401 
402 /*
403  * Return TRUE when running on Windows 95 (or 98 or ME).
404  * Only to be used after mch_init().
405  */
406     int
407 mch_windows95(void)
408 {
409     return g_PlatformId == VER_PLATFORM_WIN32_WINDOWS;
410 }
411 
412 #ifdef FEAT_GUI_W32
413 /*
414  * Used to work around the "can't do synchronous spawn"
415  * problem on Win32s, without resorting to Universal Thunk.
416  */
417 static int old_num_windows;
418 static int num_windows;
419 
420     static BOOL CALLBACK
421 win32ssynch_cb(HWND hwnd, LPARAM lparam)
422 {
423     num_windows++;
424     return TRUE;
425 }
426 #endif
427 
428 #ifndef FEAT_GUI_W32
429 
430 #define SHIFT  (SHIFT_PRESSED)
431 #define CTRL   (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
432 #define ALT    (RIGHT_ALT_PRESSED  | LEFT_ALT_PRESSED)
433 #define ALT_GR (RIGHT_ALT_PRESSED  | LEFT_CTRL_PRESSED)
434 
435 
436 /* When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
437  * We map function keys to their ANSI terminal equivalents, as produced
438  * by ANSI.SYS, for compatibility with the MS-DOS version of Vim.  Any
439  * ANSI key with a value >= '\300' is nonstandard, but provided anyway
440  * so that the user can have access to all SHIFT-, CTRL-, and ALT-
441  * combinations of function/arrow/etc keys.
442  */
443 
444 const static struct
445 {
446     WORD    wVirtKey;
447     BOOL    fAnsiKey;
448     int	    chAlone;
449     int	    chShift;
450     int	    chCtrl;
451     int	    chAlt;
452 } VirtKeyMap[] =
453 {
454 
455 /*    Key	ANSI	alone	shift	ctrl	    alt */
456     { VK_ESCAPE,FALSE,	ESC,	ESC,	ESC,	    ESC,    },
457 
458     { VK_F1,	TRUE,	';',	'T',	'^',	    'h', },
459     { VK_F2,	TRUE,	'<',	'U',	'_',	    'i', },
460     { VK_F3,	TRUE,	'=',	'V',	'`',	    'j', },
461     { VK_F4,	TRUE,	'>',	'W',	'a',	    'k', },
462     { VK_F5,	TRUE,	'?',	'X',	'b',	    'l', },
463     { VK_F6,	TRUE,	'@',	'Y',	'c',	    'm', },
464     { VK_F7,	TRUE,	'A',	'Z',	'd',	    'n', },
465     { VK_F8,	TRUE,	'B',	'[',	'e',	    'o', },
466     { VK_F9,	TRUE,	'C',	'\\',	'f',	    'p', },
467     { VK_F10,	TRUE,	'D',	']',	'g',	    'q', },
468     { VK_F11,	TRUE,	'\205',	'\207',	'\211',	    '\213', },
469     { VK_F12,	TRUE,	'\206',	'\210',	'\212',	    '\214', },
470 
471     { VK_HOME,	TRUE,	'G',	'\302',	'w',	    '\303', },
472     { VK_UP,	TRUE,	'H',	'\304',	'\305',	    '\306', },
473     { VK_PRIOR,	TRUE,	'I',	'\307',	'\204',	    '\310', }, /*PgUp*/
474     { VK_LEFT,	TRUE,	'K',	'\311',	's',	    '\312', },
475     { VK_RIGHT,	TRUE,	'M',	'\313',	't',	    '\314', },
476     { VK_END,	TRUE,	'O',	'\315',	'u',	    '\316', },
477     { VK_DOWN,	TRUE,	'P',	'\317',	'\320',	    '\321', },
478     { VK_NEXT,	TRUE,	'Q',	'\322',	'v',	    '\323', }, /*PgDn*/
479     { VK_INSERT,TRUE,	'R',	'\324',	'\325',	    '\326', },
480     { VK_DELETE,TRUE,	'S',	'\327',	'\330',	    '\331', },
481 
482     { VK_SNAPSHOT,TRUE,	0,	0,	0,	    'r', }, /*PrtScrn*/
483 
484 #if 0
485     /* Most people don't have F13-F20, but what the hell... */
486     { VK_F13,	TRUE,	'\332',	'\333',	'\334',	    '\335', },
487     { VK_F14,	TRUE,	'\336',	'\337',	'\340',	    '\341', },
488     { VK_F15,	TRUE,	'\342',	'\343',	'\344',	    '\345', },
489     { VK_F16,	TRUE,	'\346',	'\347',	'\350',	    '\351', },
490     { VK_F17,	TRUE,	'\352',	'\353',	'\354',	    '\355', },
491     { VK_F18,	TRUE,	'\356',	'\357',	'\360',	    '\361', },
492     { VK_F19,	TRUE,	'\362',	'\363',	'\364',	    '\365', },
493     { VK_F20,	TRUE,	'\366',	'\367',	'\370',	    '\371', },
494 #endif
495     { VK_ADD,	TRUE,   'N',    'N',    'N',	'N',	}, /* keyp '+' */
496     { VK_SUBTRACT, TRUE,'J',	'J',    'J',	'J',	}, /* keyp '-' */
497  /* { VK_DIVIDE,   TRUE,'N',	'N',    'N',	'N',	},    keyp '/' */
498     { VK_MULTIPLY, TRUE,'7',	'7',    '7',	'7',	}, /* keyp '*' */
499 
500     { VK_NUMPAD0,TRUE,  '\332',	'\333',	'\334',	    '\335', },
501     { VK_NUMPAD1,TRUE,  '\336',	'\337',	'\340',	    '\341', },
502     { VK_NUMPAD2,TRUE,  '\342',	'\343',	'\344',	    '\345', },
503     { VK_NUMPAD3,TRUE,  '\346',	'\347',	'\350',	    '\351', },
504     { VK_NUMPAD4,TRUE,  '\352',	'\353',	'\354',	    '\355', },
505     { VK_NUMPAD5,TRUE,  '\356',	'\357',	'\360',	    '\361', },
506     { VK_NUMPAD6,TRUE,  '\362',	'\363',	'\364',	    '\365', },
507     { VK_NUMPAD7,TRUE,  '\366',	'\367',	'\370',	    '\371', },
508     { VK_NUMPAD8,TRUE,  '\372',	'\373',	'\374',	    '\375', },
509     /* Sorry, out of number space! <negri>*/
510     { VK_NUMPAD9,TRUE,  '\376',	'\377',	'\377',	    '\367', },
511 
512 };
513 
514 
515 #ifdef _MSC_VER
516 // The ToAscii bug destroys several registers.	Need to turn off optimization
517 // or the GetConsoleKeyboardLayoutName hack will fail in non-debug versions
518 # pragma optimize("", off)
519 #endif
520 
521 #if defined(__GNUC__) && !defined(__MINGW32__)  && !defined(__CYGWIN__)
522 # define AChar AsciiChar
523 #else
524 # define AChar uChar.AsciiChar
525 #endif
526 
527 /* The return code indicates key code size. */
528     static int
529 #ifdef __BORLANDC__
530     __stdcall
531 #endif
532 win32_kbd_patch_key(
533     KEY_EVENT_RECORD* pker)
534 {
535     UINT uMods = pker->dwControlKeyState;
536     static int s_iIsDead = 0;
537     static WORD awAnsiCode[2];
538     static BYTE abKeystate[256];
539 
540 
541     if (s_iIsDead == 2)
542     {
543 	pker->AChar = (CHAR) awAnsiCode[1];
544 	s_iIsDead = 0;
545 	return 1;
546     }
547 
548     if (pker->AChar != 0)
549 	return 1;
550 
551     memset(abKeystate, 0, sizeof (abKeystate));
552 
553     // Should only be non-NULL on NT 4.0
554     if (s_pfnGetConsoleKeyboardLayoutName != NULL)
555     {
556 	CHAR szKLID[KL_NAMELENGTH];
557 
558 	if ((*s_pfnGetConsoleKeyboardLayoutName)(szKLID))
559 	    (void)LoadKeyboardLayout(szKLID, KLF_ACTIVATE);
560     }
561 
562     /* Clear any pending dead keys */
563     ToAscii(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 0);
564 
565     if (uMods & SHIFT_PRESSED)
566 	abKeystate[VK_SHIFT] = 0x80;
567     if (uMods & CAPSLOCK_ON)
568 	abKeystate[VK_CAPITAL] = 1;
569 
570     if ((uMods & ALT_GR) == ALT_GR)
571     {
572 	abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
573 	    abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
574     }
575 
576     s_iIsDead = ToAscii(pker->wVirtualKeyCode, pker->wVirtualScanCode,
577 			abKeystate, awAnsiCode, 0);
578 
579     if (s_iIsDead > 0)
580 	pker->AChar = (CHAR) awAnsiCode[0];
581 
582     return s_iIsDead;
583 }
584 
585 #ifdef _MSC_VER
586 /* MUST switch optimization on again here, otherwise a call to
587  * decode_key_event() may crash (e.g. when hitting caps-lock) */
588 # pragma optimize("", on)
589 
590 # if (_MSC_VER < 1100)
591 /* MUST turn off global optimisation for this next function, or
592  * pressing ctrl-minus in insert mode crashes Vim when built with
593  * VC4.1. -- negri. */
594 #  pragma optimize("g", off)
595 # endif
596 #endif
597 
598 static BOOL g_fJustGotFocus = FALSE;
599 
600 /*
601  * Decode a KEY_EVENT into one or two keystrokes
602  */
603     static BOOL
604 decode_key_event(
605     KEY_EVENT_RECORD	*pker,
606     char_u		*pch,
607     char_u		*pch2,
608     int			*pmodifiers,
609     BOOL		fDoPost)
610 {
611     int i;
612     const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
613 
614     *pch = *pch2 = NUL;
615     g_fJustGotFocus = FALSE;
616 
617     /* ignore key up events */
618     if (!pker->bKeyDown)
619 	return FALSE;
620 
621     /* ignore some keystrokes */
622     switch (pker->wVirtualKeyCode)
623     {
624     /* modifiers */
625     case VK_SHIFT:
626     case VK_CONTROL:
627     case VK_MENU:   /* Alt key */
628 	return FALSE;
629 
630     default:
631 	break;
632     }
633 
634     /* special cases */
635     if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->AChar == NUL)
636     {
637 	/* Ctrl-6 is Ctrl-^ */
638 	if (pker->wVirtualKeyCode == '6')
639 	{
640 	    *pch = Ctrl_HAT;
641 	    return TRUE;
642 	}
643 	/* Ctrl-2 is Ctrl-@ */
644 	else if (pker->wVirtualKeyCode == '2')
645 	{
646 	    *pch = NUL;
647 	    return TRUE;
648 	}
649 	/* Ctrl-- is Ctrl-_ */
650 	else if (pker->wVirtualKeyCode == 0xBD)
651 	{
652 	    *pch = Ctrl__;
653 	    return TRUE;
654 	}
655     }
656 
657     /* Shift-TAB */
658     if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED))
659     {
660 	*pch = K_NUL;
661 	*pch2 = '\017';
662 	return TRUE;
663     }
664 
665     for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]);  --i >= 0;  )
666     {
667 	if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
668 	{
669 	    if (nModifs == 0)
670 		*pch = VirtKeyMap[i].chAlone;
671 	    else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
672 		*pch = VirtKeyMap[i].chShift;
673 	    else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
674 		*pch = VirtKeyMap[i].chCtrl;
675 	    else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
676 		*pch = VirtKeyMap[i].chAlt;
677 
678 	    if (*pch != 0)
679 	    {
680 		if (VirtKeyMap[i].fAnsiKey)
681 		{
682 		    *pch2 = *pch;
683 		    *pch = K_NUL;
684 		}
685 
686 		return TRUE;
687 	    }
688 	}
689     }
690 
691     i = win32_kbd_patch_key(pker);
692 
693     if (i < 0)
694 	*pch = NUL;
695     else
696     {
697 	*pch = (i > 0) ? pker->AChar : NUL;
698 
699 	if (pmodifiers != NULL)
700 	{
701 	    /* Pass on the ALT key as a modifier, but only when not combined
702 	     * with CTRL (which is ALTGR). */
703 	    if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0)
704 		*pmodifiers |= MOD_MASK_ALT;
705 
706 	    /* Pass on SHIFT only for special keys, because we don't know when
707 	     * it's already included with the character. */
708 	    if ((nModifs & SHIFT) != 0 && *pch <= 0x20)
709 		*pmodifiers |= MOD_MASK_SHIFT;
710 
711 	    /* Pass on CTRL only for non-special keys, because we don't know
712 	     * when it's already included with the character.  And not when
713 	     * combined with ALT (which is ALTGR). */
714 	    if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0
715 					       && *pch >= 0x20 && *pch < 0x80)
716 		*pmodifiers |= MOD_MASK_CTRL;
717 	}
718     }
719 
720     return (*pch != NUL);
721 }
722 
723 #ifdef _MSC_VER
724 # pragma optimize("", on)
725 #endif
726 
727 #endif /* FEAT_GUI_W32 */
728 
729 
730 #ifdef FEAT_MOUSE
731 
732 /*
733  * For the GUI the mouse handling is in gui_w32.c.
734  */
735 # ifdef FEAT_GUI_W32
736     void
737 mch_setmouse(
738     int on)
739 {
740 }
741 # else
742 static int g_fMouseAvail = FALSE;   /* mouse present */
743 static int g_fMouseActive = FALSE;  /* mouse enabled */
744 static int g_nMouseClick = -1;	    /* mouse status */
745 static int g_xMouse;		    /* mouse x coordinate */
746 static int g_yMouse;		    /* mouse y coordinate */
747 
748 /*
749  * Enable or disable mouse input
750  */
751     void
752 mch_setmouse(
753     int on)
754 {
755     DWORD cmodein;
756 
757     if (!g_fMouseAvail)
758 	return;
759 
760     g_fMouseActive = on;
761     GetConsoleMode(g_hConIn, &cmodein);
762 
763     if (g_fMouseActive)
764 	cmodein |= ENABLE_MOUSE_INPUT;
765     else
766 	cmodein &= ~ENABLE_MOUSE_INPUT;
767 
768     SetConsoleMode(g_hConIn, cmodein);
769 }
770 
771 
772 /*
773  * Decode a MOUSE_EVENT.  If it's a valid event, return MOUSE_LEFT,
774  * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
775  * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
776  * or a MOUSE_LEFT, _MIDDLE, or _RIGHT.  We encode the button type,
777  * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
778  * and we return the mouse position in g_xMouse and g_yMouse.
779  *
780  * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
781  * MOUSE_DRAGs and one MOUSE_RELEASE.  MOUSE_RELEASE will be followed only
782  * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
783  *
784  * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
785  * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
786  *
787  * Windows will send us MOUSE_MOVED notifications whenever the mouse
788  * moves, even if it stays within the same character cell.  We ignore
789  * all MOUSE_MOVED messages if the position hasn't really changed, and
790  * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
791  * we're only interested in MOUSE_DRAG).
792  *
793  * All of this is complicated by the code that fakes MOUSE_MIDDLE on
794  * 2-button mouses by pressing the left & right buttons simultaneously.
795  * In practice, it's almost impossible to click both at the same time,
796  * so we need to delay a little.  Also, we tend not to get MOUSE_RELEASE
797  * in such cases, if the user is clicking quickly.
798  */
799     static BOOL
800 decode_mouse_event(
801     MOUSE_EVENT_RECORD* pmer)
802 {
803     static int s_nOldButton = -1;
804     static int s_nOldMouseClick = -1;
805     static int s_xOldMouse = -1;
806     static int s_yOldMouse = -1;
807     static linenr_T s_old_topline = 0;
808 #ifdef FEAT_DIFF
809     static int s_old_topfill = 0;
810 #endif
811     static int s_cClicks = 1;
812     static BOOL s_fReleased = TRUE;
813     static DWORD s_dwLastClickTime = 0;
814     static BOOL s_fNextIsMiddle = FALSE;
815 
816     static DWORD cButtons = 0;	/* number of buttons supported */
817 
818     const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
819     const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
820     const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
821     const DWORD LEFT_RIGHT = LEFT | RIGHT;
822 
823     int nButton;
824 
825     if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons))
826 	cButtons = 2;
827 
828     if (!g_fMouseAvail || !g_fMouseActive)
829     {
830 	g_nMouseClick = -1;
831 	return FALSE;
832     }
833 
834     /* get a spurious MOUSE_EVENT immediately after receiving focus; ignore */
835     if (g_fJustGotFocus)
836     {
837 	g_fJustGotFocus = FALSE;
838 	return FALSE;
839     }
840 
841     /* unprocessed mouse click? */
842     if (g_nMouseClick != -1)
843 	return TRUE;
844 
845     nButton = -1;
846     g_xMouse = pmer->dwMousePosition.X;
847     g_yMouse = pmer->dwMousePosition.Y;
848 
849     if (pmer->dwEventFlags == MOUSE_MOVED)
850     {
851 	/* ignore MOUSE_MOVED events if (x, y) hasn't changed.	(We get these
852 	 * events even when the mouse moves only within a char cell.) */
853 	if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse)
854 	    return FALSE;
855     }
856 
857     /* If no buttons are pressed... */
858     if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0)
859     {
860 	/* If the last thing returned was MOUSE_RELEASE, ignore this */
861 	if (s_fReleased)
862 	    return FALSE;
863 
864 	nButton = MOUSE_RELEASE;
865 	s_fReleased = TRUE;
866     }
867     else    /* one or more buttons pressed */
868     {
869 	/* on a 2-button mouse, hold down left and right buttons
870 	 * simultaneously to get MIDDLE. */
871 
872 	if (cButtons == 2 && s_nOldButton != MOUSE_DRAG)
873 	{
874 	    DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
875 
876 	    /* if either left or right button only is pressed, see if the
877 	     * the next mouse event has both of them pressed */
878 	    if (dwLR == LEFT || dwLR == RIGHT)
879 	    {
880 		for (;;)
881 		{
882 		    /* wait a short time for next input event */
883 		    if (WaitForSingleObject(g_hConIn, p_mouset / 3)
884 							     != WAIT_OBJECT_0)
885 			break;
886 		    else
887 		    {
888 			DWORD cRecords = 0;
889 			INPUT_RECORD ir;
890 			MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
891 
892 			PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
893 
894 			if (cRecords == 0 || ir.EventType != MOUSE_EVENT
895 				|| !(pmer2->dwButtonState & LEFT_RIGHT))
896 			    break;
897 			else
898 			{
899 			    if (pmer2->dwEventFlags != MOUSE_MOVED)
900 			    {
901 				ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
902 
903 				return decode_mouse_event(pmer2);
904 			    }
905 			    else if (s_xOldMouse == pmer2->dwMousePosition.X &&
906 				     s_yOldMouse == pmer2->dwMousePosition.Y)
907 			    {
908 				/* throw away spurious mouse move */
909 				ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
910 
911 				/* are there any more mouse events in queue? */
912 				PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
913 
914 				if (cRecords==0 || ir.EventType != MOUSE_EVENT)
915 				    break;
916 			    }
917 			    else
918 				break;
919 			}
920 		    }
921 		}
922 	    }
923 	}
924 
925 	if (s_fNextIsMiddle)
926 	{
927 	    nButton = (pmer->dwEventFlags == MOUSE_MOVED)
928 		? MOUSE_DRAG : MOUSE_MIDDLE;
929 	    s_fNextIsMiddle = FALSE;
930 	}
931 	else if (cButtons == 2	&&
932 	    ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
933 	{
934 	    nButton = MOUSE_MIDDLE;
935 
936 	    if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED)
937 	    {
938 		s_fNextIsMiddle = TRUE;
939 		nButton = MOUSE_RELEASE;
940 	    }
941 	}
942 	else if ((pmer->dwButtonState & LEFT) == LEFT)
943 	    nButton = MOUSE_LEFT;
944 	else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
945 	    nButton = MOUSE_MIDDLE;
946 	else if ((pmer->dwButtonState & RIGHT) == RIGHT)
947 	    nButton = MOUSE_RIGHT;
948 
949 	if (! s_fReleased && ! s_fNextIsMiddle
950 		&& nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG)
951 	    return FALSE;
952 
953 	s_fReleased = s_fNextIsMiddle;
954     }
955 
956     if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK)
957     {
958 	/* button pressed or released, without mouse moving */
959 	if (nButton != -1 && nButton != MOUSE_RELEASE)
960 	{
961 	    DWORD dwCurrentTime = GetTickCount();
962 
963 	    if (s_xOldMouse != g_xMouse
964 		    || s_yOldMouse != g_yMouse
965 		    || s_nOldButton != nButton
966 		    || s_old_topline != curwin->w_topline
967 #ifdef FEAT_DIFF
968 		    || s_old_topfill != curwin->w_topfill
969 #endif
970 		    || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset)
971 	    {
972 		s_cClicks = 1;
973 	    }
974 	    else if (++s_cClicks > 4)
975 	    {
976 		s_cClicks = 1;
977 	    }
978 
979 	    s_dwLastClickTime = dwCurrentTime;
980 	}
981     }
982     else if (pmer->dwEventFlags == MOUSE_MOVED)
983     {
984 	if (nButton != -1 && nButton != MOUSE_RELEASE)
985 	    nButton = MOUSE_DRAG;
986 
987 	s_cClicks = 1;
988     }
989 
990     if (nButton == -1)
991 	return FALSE;
992 
993     if (nButton != MOUSE_RELEASE)
994 	s_nOldButton = nButton;
995 
996     g_nMouseClick = nButton;
997 
998     if (pmer->dwControlKeyState & SHIFT_PRESSED)
999 	g_nMouseClick |= MOUSE_SHIFT;
1000     if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
1001 	g_nMouseClick |= MOUSE_CTRL;
1002     if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED  | LEFT_ALT_PRESSED))
1003 	g_nMouseClick |= MOUSE_ALT;
1004 
1005     if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE)
1006 	SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
1007 
1008     /* only pass on interesting (i.e., different) mouse events */
1009     if (s_xOldMouse == g_xMouse
1010 	    && s_yOldMouse == g_yMouse
1011 	    && s_nOldMouseClick == g_nMouseClick)
1012     {
1013 	g_nMouseClick = -1;
1014 	return FALSE;
1015     }
1016 
1017     s_xOldMouse = g_xMouse;
1018     s_yOldMouse = g_yMouse;
1019     s_old_topline = curwin->w_topline;
1020 #ifdef FEAT_DIFF
1021     s_old_topfill = curwin->w_topfill;
1022 #endif
1023     s_nOldMouseClick = g_nMouseClick;
1024 
1025     return TRUE;
1026 }
1027 
1028 # endif /* FEAT_GUI_W32 */
1029 #endif /* FEAT_MOUSE */
1030 
1031 
1032 #ifdef MCH_CURSOR_SHAPE
1033 /*
1034  * Set the shape of the cursor.
1035  * 'thickness' can be from 1 (thin) to 99 (block)
1036  */
1037     static void
1038 mch_set_cursor_shape(int thickness)
1039 {
1040     CONSOLE_CURSOR_INFO ConsoleCursorInfo;
1041     ConsoleCursorInfo.dwSize = thickness;
1042     ConsoleCursorInfo.bVisible = s_cursor_visible;
1043 
1044     SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo);
1045     if (s_cursor_visible)
1046 	SetConsoleCursorPosition(g_hConOut, g_coord);
1047 }
1048 
1049     void
1050 mch_update_cursor(void)
1051 {
1052     int		idx;
1053     int		thickness;
1054 
1055     /*
1056      * How the cursor is drawn depends on the current mode.
1057      */
1058     idx = get_shape_idx(FALSE);
1059 
1060     if (shape_table[idx].shape == SHAPE_BLOCK)
1061 	thickness = 99;	/* 100 doesn't work on W95 */
1062     else
1063 	thickness = shape_table[idx].percentage;
1064     mch_set_cursor_shape(thickness);
1065 }
1066 #endif
1067 
1068 #ifndef FEAT_GUI_W32	    /* this isn't used for the GUI */
1069 /*
1070  * Handle FOCUS_EVENT.
1071  */
1072     static void
1073 handle_focus_event(INPUT_RECORD ir)
1074 {
1075     g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
1076     ui_focus_change((int)g_fJustGotFocus);
1077 }
1078 
1079 /*
1080  * Wait until console input from keyboard or mouse is available,
1081  * or the time is up.
1082  * Return TRUE if something is available FALSE if not.
1083  */
1084     static int
1085 WaitForChar(long msec)
1086 {
1087     DWORD	    dwNow = 0, dwEndTime = 0;
1088     INPUT_RECORD    ir;
1089     DWORD	    cRecords;
1090     char_u	    ch, ch2;
1091 
1092     if (msec > 0)
1093 	/* Wait until the specified time has elapsed. */
1094 	dwEndTime = GetTickCount() + msec;
1095     else if (msec < 0)
1096 	/* Wait forever. */
1097 	dwEndTime = INFINITE;
1098 
1099     /* We need to loop until the end of the time period, because
1100      * we might get multiple unusable mouse events in that time.
1101      */
1102     for (;;)
1103     {
1104 #ifdef FEAT_MZSCHEME
1105 	mzvim_check_threads();
1106 #endif
1107 #ifdef FEAT_CLIENTSERVER
1108 	serverProcessPendingMessages();
1109 #endif
1110 	if (0
1111 #ifdef FEAT_MOUSE
1112 		|| g_nMouseClick != -1
1113 #endif
1114 #ifdef FEAT_CLIENTSERVER
1115 		|| input_available()
1116 #endif
1117 	   )
1118 	    return TRUE;
1119 
1120 	if (msec > 0)
1121 	{
1122 	    /* If the specified wait time has passed, return. */
1123 	    dwNow = GetTickCount();
1124 	    if (dwNow >= dwEndTime)
1125 		break;
1126 	}
1127 	if (msec != 0)
1128 	{
1129 	    DWORD dwWaitTime = dwEndTime - dwNow;
1130 
1131 #ifdef FEAT_MZSCHEME
1132 	    if (mzthreads_allowed() && p_mzq > 0
1133 				    && (msec < 0 || (long)dwWaitTime > p_mzq))
1134 		dwWaitTime = p_mzq; /* don't wait longer than 'mzquantum' */
1135 #endif
1136 #ifdef FEAT_CLIENTSERVER
1137 	    /* Wait for either an event on the console input or a message in
1138 	     * the client-server window. */
1139 	    if (MsgWaitForMultipleObjects(1, &g_hConIn, FALSE,
1140 				 dwWaitTime, QS_SENDMESSAGE) != WAIT_OBJECT_0)
1141 #else
1142 	    if (WaitForSingleObject(g_hConIn, dwWaitTime) != WAIT_OBJECT_0)
1143 #endif
1144 		    continue;
1145 	}
1146 
1147 	cRecords = 0;
1148 	PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
1149 
1150 #ifdef FEAT_MBYTE_IME
1151 	if (State & CMDLINE && msg_row == Rows - 1)
1152 	{
1153 	    CONSOLE_SCREEN_BUFFER_INFO csbi;
1154 
1155 	    if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1156 	    {
1157 		if (csbi.dwCursorPosition.Y != msg_row)
1158 		{
1159 		    /* The screen is now messed up, must redraw the
1160 		     * command line and later all the windows. */
1161 		    redraw_all_later(CLEAR);
1162 		    cmdline_row -= (msg_row - csbi.dwCursorPosition.Y);
1163 		    redrawcmd();
1164 		}
1165 	    }
1166 	}
1167 #endif
1168 
1169 	if (cRecords > 0)
1170 	{
1171 	    if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
1172 	    {
1173 #ifdef FEAT_MBYTE_IME
1174 		/* Windows IME sends two '\n's with only one 'ENTER'.  First:
1175 		 * wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */
1176 		if (ir.Event.KeyEvent.uChar.UnicodeChar == 0
1177 			&& ir.Event.KeyEvent.wVirtualKeyCode == 13)
1178 		{
1179 		    ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
1180 		    continue;
1181 		}
1182 #endif
1183 		if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
1184 								 NULL, FALSE))
1185 		    return TRUE;
1186 	    }
1187 
1188 	    ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
1189 
1190 	    if (ir.EventType == FOCUS_EVENT)
1191 		handle_focus_event(ir);
1192 	    else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1193 		shell_resized();
1194 #ifdef FEAT_MOUSE
1195 	    else if (ir.EventType == MOUSE_EVENT
1196 		    && decode_mouse_event(&ir.Event.MouseEvent))
1197 		return TRUE;
1198 #endif
1199 	}
1200 	else if (msec == 0)
1201 	    break;
1202     }
1203 
1204 #ifdef FEAT_CLIENTSERVER
1205     /* Something might have been received while we were waiting. */
1206     if (input_available())
1207 	return TRUE;
1208 #endif
1209     return FALSE;
1210 }
1211 
1212 #ifndef FEAT_GUI_MSWIN
1213 /*
1214  * return non-zero if a character is available
1215  */
1216     int
1217 mch_char_avail()
1218 {
1219     return WaitForChar(0L);
1220 }
1221 #endif
1222 
1223 /*
1224  * Create the console input.  Used when reading stdin doesn't work.
1225  */
1226     static void
1227 create_conin(void)
1228 {
1229     g_hConIn =	CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
1230 			FILE_SHARE_READ|FILE_SHARE_WRITE,
1231 			(LPSECURITY_ATTRIBUTES) NULL,
1232 			OPEN_EXISTING, (DWORD)NULL, (HANDLE)NULL);
1233     did_create_conin = TRUE;
1234 }
1235 
1236 /*
1237  * Get a keystroke or a mouse event
1238  */
1239     static char_u
1240 tgetch(int *pmodifiers, char_u *pch2)
1241 {
1242     char_u ch;
1243 
1244     for (;;)
1245     {
1246 	INPUT_RECORD ir;
1247 	DWORD cRecords = 0;
1248 
1249 #ifdef FEAT_CLIENTSERVER
1250 	(void)WaitForChar(-1L);
1251 	if (input_available())
1252 	    return 0;
1253 # ifdef FEAT_MOUSE
1254 	if (g_nMouseClick != -1)
1255 	    return 0;
1256 # endif
1257 #endif
1258 	if (ReadConsoleInput(g_hConIn, &ir, 1, &cRecords) == 0)
1259 	{
1260 	    if (did_create_conin)
1261 		read_error_exit();
1262 	    create_conin();
1263 	    continue;
1264 	}
1265 
1266 	if (ir.EventType == KEY_EVENT)
1267 	{
1268 	    if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2,
1269 							    pmodifiers, TRUE))
1270 		return ch;
1271 	}
1272 	else if (ir.EventType == FOCUS_EVENT)
1273 	    handle_focus_event(ir);
1274 	else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1275 	    shell_resized();
1276 #ifdef FEAT_MOUSE
1277 	else if (ir.EventType == MOUSE_EVENT)
1278 	{
1279 	    if (decode_mouse_event(&ir.Event.MouseEvent))
1280 		return 0;
1281 	}
1282 #endif
1283     }
1284 }
1285 #endif /* !FEAT_GUI_W32 */
1286 
1287 
1288 /*
1289  * mch_inchar(): low-level input funcion.
1290  * Get one or more characters from the keyboard or the mouse.
1291  * If time == 0, do not wait for characters.
1292  * If time == n, wait a short time for characters.
1293  * If time == -1, wait forever for characters.
1294  * Returns the number of characters read into buf.
1295  */
1296     int
1297 mch_inchar(
1298     char_u	*buf,
1299     int		maxlen,
1300     long	time,
1301     int		tb_change_cnt)
1302 {
1303 #ifndef FEAT_GUI_W32	    /* this isn't used for the GUI */
1304 
1305     int		len;
1306     int		c;
1307 #ifdef FEAT_AUTOCMD
1308     static int	once_already = 0;
1309 #endif
1310 #define TYPEAHEADLEN 20
1311     static char_u   typeahead[TYPEAHEADLEN];	/* previously typed bytes. */
1312     static int	    typeaheadlen = 0;
1313 
1314     /* First use any typeahead that was kept because "buf" was too small. */
1315     if (typeaheadlen > 0)
1316 	goto theend;
1317 
1318 #ifdef FEAT_SNIFF
1319     if (want_sniff_request)
1320     {
1321 	if (sniff_request_waiting)
1322 	{
1323 	    /* return K_SNIFF */
1324 	    typeahead[typeaheadlen++] = CSI;
1325 	    typeahead[typeaheadlen++] = (char_u)KS_EXTRA;
1326 	    typeahead[typeaheadlen++] = (char_u)KE_SNIFF;
1327 	    sniff_request_waiting = 0;
1328 	    want_sniff_request = 0;
1329 	    goto theend;
1330 	}
1331 	else if (time < 0 || time > 250)
1332 	{
1333 	    /* don't wait too long, a request might be pending */
1334 	    time = 250;
1335 	}
1336     }
1337 #endif
1338 
1339     if (time >= 0)
1340     {
1341 	if (!WaitForChar(time))     /* no character available */
1342 	{
1343 #ifdef FEAT_AUTOCMD
1344 	    once_already = 0;
1345 #endif
1346 	    return 0;
1347 	}
1348     }
1349     else    /* time == -1, wait forever */
1350     {
1351 	mch_set_winsize_now();	/* Allow winsize changes from now on */
1352 
1353 #ifdef FEAT_AUTOCMD
1354 	/* If there is no character available within 2 seconds (default),
1355 	 * write the autoscript file to disk */
1356 	if (once_already == 2)
1357 	    updatescript(0);
1358 	else if (once_already == 1)
1359 	{
1360 	    setcursor();
1361 	    once_already = 2;
1362 	    return 0;
1363 	}
1364 	else
1365 #endif
1366 	    if (!WaitForChar(p_ut))
1367 	{
1368 #ifdef FEAT_AUTOCMD
1369 	    if (has_cursorhold() && get_real_state() == NORMAL_BUSY)
1370 	    {
1371 		apply_autocmds(EVENT_CURSORHOLD, NULL, NULL, FALSE, curbuf);
1372 		update_screen(VALID);
1373 		once_already = 1;
1374 		return 0;
1375 	    }
1376 #endif
1377 	    updatescript(0);
1378 	}
1379     }
1380 
1381     /*
1382      * Try to read as many characters as there are, until the buffer is full.
1383      */
1384 
1385     /* we will get at least one key. Get more if they are available. */
1386     g_fCBrkPressed = FALSE;
1387 
1388 #ifdef MCH_WRITE_DUMP
1389     if (fdDump)
1390 	fputc('[', fdDump);
1391 #endif
1392 
1393     /* Keep looping until there is something in the typeahead buffer and more
1394      * to get and still room in the buffer (up to two bytes for a char and
1395      * three bytes for a modifier). */
1396     while ((typeaheadlen == 0 || WaitForChar(0L))
1397 					  && typeaheadlen + 5 <= TYPEAHEADLEN)
1398     {
1399 	if (typebuf_changed(tb_change_cnt))
1400 	{
1401 	    /* "buf" may be invalid now if a client put something in the
1402 	     * typeahead buffer and "buf" is in the typeahead buffer. */
1403 	    typeaheadlen = 0;
1404 	    break;
1405 	}
1406 #ifdef FEAT_MOUSE
1407 	if (g_nMouseClick != -1)
1408 	{
1409 # ifdef MCH_WRITE_DUMP
1410 	    if (fdDump)
1411 		fprintf(fdDump, "{%02x @ %d, %d}",
1412 			g_nMouseClick, g_xMouse, g_yMouse);
1413 # endif
1414 	    typeahead[typeaheadlen++] = ESC + 128;
1415 	    typeahead[typeaheadlen++] = 'M';
1416 	    typeahead[typeaheadlen++] = g_nMouseClick;
1417 	    typeahead[typeaheadlen++] = g_xMouse + '!';
1418 	    typeahead[typeaheadlen++] = g_yMouse + '!';
1419 	    g_nMouseClick = -1;
1420 	}
1421 	else
1422 #endif
1423 	{
1424 	    char_u	ch2 = NUL;
1425 	    int		modifiers = 0;
1426 
1427 	    c = tgetch(&modifiers, &ch2);
1428 
1429 	    if (typebuf_changed(tb_change_cnt))
1430 	    {
1431 		/* "buf" may be invalid now if a client put something in the
1432 		 * typeahead buffer and "buf" is in the typeahead buffer. */
1433 		typeaheadlen = 0;
1434 		break;
1435 	    }
1436 
1437 	    if (c == Ctrl_C && ctrl_c_interrupts)
1438 	    {
1439 #if defined(FEAT_CLIENTSERVER)
1440 		trash_input_buf();
1441 #endif
1442 		got_int = TRUE;
1443 	    }
1444 
1445 #ifdef FEAT_MOUSE
1446 	    if (g_nMouseClick == -1)
1447 #endif
1448 	    {
1449 		int	n = 1;
1450 
1451 		/* A key may have one or two bytes. */
1452 		typeahead[typeaheadlen] = c;
1453 		if (ch2 != NUL)
1454 		{
1455 		    typeahead[typeaheadlen + 1] = ch2;
1456 		    ++n;
1457 		}
1458 #ifdef FEAT_MBYTE
1459 		/* Only convert normal characters, not special keys.  Need to
1460 		 * convert before applying ALT, otherwise mapping <M-x> breaks
1461 		 * when 'tenc' is set. */
1462 		if (input_conv.vc_type != CONV_NONE
1463 						&& (ch2 == NUL || c != K_NUL))
1464 		    n = convert_input(typeahead + typeaheadlen, n,
1465 						 TYPEAHEADLEN - typeaheadlen);
1466 #endif
1467 
1468 		/* Use the ALT key to set the 8th bit of the character
1469 		 * when it's one byte, the 8th bit isn't set yet and not
1470 		 * using a double-byte encoding (would become a lead
1471 		 * byte). */
1472 		if ((modifiers & MOD_MASK_ALT)
1473 			&& n == 1
1474 			&& (typeahead[typeaheadlen] & 0x80) == 0
1475 #ifdef FEAT_MBYTE
1476 			&& !enc_dbcs
1477 #endif
1478 		   )
1479 		{
1480 		    typeahead[typeaheadlen] |= 0x80;
1481 		    modifiers &= ~MOD_MASK_ALT;
1482 		}
1483 
1484 		if (modifiers != 0)
1485 		{
1486 		    /* Prepend modifiers to the character. */
1487 		    mch_memmove(typeahead + typeaheadlen + 3,
1488 						 typeahead + typeaheadlen, n);
1489 		    typeahead[typeaheadlen++] = K_SPECIAL;
1490 		    typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
1491 		    typeahead[typeaheadlen++] =  modifiers;
1492 		}
1493 
1494 		typeaheadlen += n;
1495 
1496 #ifdef MCH_WRITE_DUMP
1497 		if (fdDump)
1498 		    fputc(c, fdDump);
1499 #endif
1500 	    }
1501 	}
1502     }
1503 
1504 #ifdef MCH_WRITE_DUMP
1505     if (fdDump)
1506     {
1507 	fputs("]\n", fdDump);
1508 	fflush(fdDump);
1509     }
1510 #endif
1511 
1512 #ifdef FEAT_AUTOCMD
1513     once_already = 0;
1514 #endif
1515 
1516 theend:
1517     /* Move typeahead to "buf", as much as fits. */
1518     len = 0;
1519     while (len < maxlen && typeaheadlen > 0)
1520     {
1521 	buf[len++] = typeahead[0];
1522 	mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
1523     }
1524     return len;
1525 
1526 #else /* FEAT_GUI_W32 */
1527     return 0;
1528 #endif /* FEAT_GUI_W32 */
1529 }
1530 
1531 #ifndef __MINGW32__
1532 # include <shellapi.h>	/* required for FindExecutable() */
1533 #endif
1534 
1535 /*
1536  * Return TRUE if "name" is in $PATH.
1537  * TODO: Should somehow check if it's really executable.
1538  */
1539     static int
1540 executable_exists(char *name)
1541 {
1542     char	*dum;
1543     char	fname[_MAX_PATH];
1544 
1545 #ifdef FEAT_MBYTE
1546     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
1547     {
1548 	WCHAR	*p = enc_to_ucs2(name, NULL);
1549 	WCHAR	fnamew[_MAX_PATH];
1550 	WCHAR	*dumw;
1551 	long	n;
1552 
1553 	if (p != NULL)
1554 	{
1555 	    n = (long)SearchPathW(NULL, p, NULL, _MAX_PATH, fnamew, &dumw);
1556 	    vim_free(p);
1557 	    if (n > 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
1558 	    {
1559 		if (n == 0)
1560 		    return FALSE;
1561 		if (GetFileAttributesW(fnamew) & FILE_ATTRIBUTE_DIRECTORY)
1562 		    return FALSE;
1563 		return TRUE;
1564 	    }
1565 	    /* Retry with non-wide function (for Windows 98). */
1566 	}
1567     }
1568 #endif
1569     if (SearchPath(NULL, name, NULL, _MAX_PATH, fname, &dum) == 0)
1570 	return FALSE;
1571     if (mch_isdir(fname))
1572 	return FALSE;
1573     return TRUE;
1574 }
1575 
1576 #ifdef FEAT_GUI_W32
1577 
1578 /*
1579  * GUI version of mch_init().
1580  */
1581     void
1582 mch_init()
1583 {
1584 #ifndef __MINGW32__
1585     extern int _fmode;
1586 #endif
1587 
1588     /* Let critical errors result in a failure, not in a dialog box.  Required
1589      * for the timestamp test to work on removed floppies. */
1590     SetErrorMode(SEM_FAILCRITICALERRORS);
1591 
1592     _fmode = O_BINARY;		/* we do our own CR-LF translation */
1593 
1594     /* Specify window size.  Is there a place to get the default from? */
1595     Rows = 25;
1596     Columns = 80;
1597 
1598     /* Look for 'vimrun' */
1599     if (!gui_is_win32s())
1600     {
1601 	char_u vimrun_location[_MAX_PATH + 4];
1602 
1603 	/* First try in same directory as gvim.exe */
1604 	STRCPY(vimrun_location, exe_name);
1605 	STRCPY(gettail(vimrun_location), "vimrun.exe");
1606 	if (mch_getperm(vimrun_location) >= 0)
1607 	{
1608 	    if (*skiptowhite(vimrun_location) != NUL)
1609 	    {
1610 		/* Enclose path with white space in double quotes. */
1611 		mch_memmove(vimrun_location + 1, vimrun_location,
1612 						 STRLEN(vimrun_location) + 1);
1613 		*vimrun_location = '"';
1614 		STRCPY(gettail(vimrun_location), "vimrun\" ");
1615 	    }
1616 	    else
1617 		STRCPY(gettail(vimrun_location), "vimrun ");
1618 
1619 	    vimrun_path = (char *)vim_strsave(vimrun_location);
1620 	    s_dont_use_vimrun = FALSE;
1621 	}
1622 	else if (executable_exists("vimrun.exe"))
1623 	    s_dont_use_vimrun = FALSE;
1624 
1625 	/* Don't give the warning for a missing vimrun.exe right now, but only
1626 	 * when vimrun was supposed to be used.  Don't bother people that do
1627 	 * not need vimrun.exe. */
1628 	if (s_dont_use_vimrun)
1629 	    need_vimrun_warning = TRUE;
1630     }
1631 
1632     /*
1633      * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
1634      * Otherwise the default "findstr /n" is used.
1635      */
1636     if (!executable_exists("findstr.exe"))
1637 	set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
1638 
1639 #ifdef FEAT_CLIPBOARD
1640     clip_init(TRUE);
1641 
1642     /*
1643      * Vim's own clipboard format recognises whether the text is char, line, or
1644      * rectangular block.  Only useful for copying between two Vims.
1645      * "VimClipboard" was used for previous versions, using the first
1646      * character to specify MCHAR, MLINE or MBLOCK.
1647      */
1648     clip_star.format = RegisterClipboardFormat("VimClipboard2");
1649     clip_star.format_raw = RegisterClipboardFormat("VimRawBytes");
1650 #endif
1651 }
1652 
1653 
1654 #else /* FEAT_GUI_W32 */
1655 
1656 #define SRWIDTH(sr) ((sr).Right - (sr).Left + 1)
1657 #define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1)
1658 
1659 /*
1660  * ClearConsoleBuffer()
1661  * Description:
1662  *  Clears the entire contents of the console screen buffer, using the
1663  *  specified attribute.
1664  * Returns:
1665  *  TRUE on success
1666  */
1667     static BOOL
1668 ClearConsoleBuffer(WORD wAttribute)
1669 {
1670     CONSOLE_SCREEN_BUFFER_INFO csbi;
1671     COORD coord;
1672     DWORD NumCells, dummy;
1673 
1674     if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1675 	return FALSE;
1676 
1677     NumCells = csbi.dwSize.X * csbi.dwSize.Y;
1678     coord.X = 0;
1679     coord.Y = 0;
1680     if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells,
1681 	    coord, &dummy))
1682     {
1683 	return FALSE;
1684     }
1685     if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells,
1686 	    coord, &dummy))
1687     {
1688 	return FALSE;
1689     }
1690 
1691     return TRUE;
1692 }
1693 
1694 /*
1695  * FitConsoleWindow()
1696  * Description:
1697  *  Checks if the console window will fit within given buffer dimensions.
1698  *  Also, if requested, will shrink the window to fit.
1699  * Returns:
1700  *  TRUE on success
1701  */
1702     static BOOL
1703 FitConsoleWindow(
1704     COORD dwBufferSize,
1705     BOOL WantAdjust)
1706 {
1707     CONSOLE_SCREEN_BUFFER_INFO csbi;
1708     COORD dwWindowSize;
1709     BOOL NeedAdjust = FALSE;
1710 
1711     if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1712     {
1713 	/*
1714 	 * A buffer resize will fail if the current console window does
1715 	 * not lie completely within that buffer.  To avoid this, we might
1716 	 * have to move and possibly shrink the window.
1717 	 */
1718 	if (csbi.srWindow.Right >= dwBufferSize.X)
1719 	{
1720 	    dwWindowSize.X = SRWIDTH(csbi.srWindow);
1721 	    if (dwWindowSize.X > dwBufferSize.X)
1722 		dwWindowSize.X = dwBufferSize.X;
1723 	    csbi.srWindow.Right = dwBufferSize.X - 1;
1724 	    csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X;
1725 	    NeedAdjust = TRUE;
1726 	}
1727 	if (csbi.srWindow.Bottom >= dwBufferSize.Y)
1728 	{
1729 	    dwWindowSize.Y = SRHEIGHT(csbi.srWindow);
1730 	    if (dwWindowSize.Y > dwBufferSize.Y)
1731 		dwWindowSize.Y = dwBufferSize.Y;
1732 	    csbi.srWindow.Bottom = dwBufferSize.Y - 1;
1733 	    csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y;
1734 	    NeedAdjust = TRUE;
1735 	}
1736 	if (NeedAdjust && WantAdjust)
1737 	{
1738 	    if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow))
1739 		return FALSE;
1740 	}
1741 	return TRUE;
1742     }
1743 
1744     return FALSE;
1745 }
1746 
1747 typedef struct ConsoleBufferStruct
1748 {
1749     BOOL IsValid;
1750     CONSOLE_SCREEN_BUFFER_INFO Info;
1751     PCHAR_INFO Buffer;
1752     COORD BufferSize;
1753 } ConsoleBuffer;
1754 
1755 /*
1756  * SaveConsoleBuffer()
1757  * Description:
1758  *  Saves important information about the console buffer, including the
1759  *  actual buffer contents.  The saved information is suitable for later
1760  *  restoration by RestoreConsoleBuffer().
1761  * Returns:
1762  *  TRUE if all information was saved; FALSE otherwise
1763  *  If FALSE, still sets cb->IsValid if buffer characteristics were saved.
1764  */
1765     static BOOL
1766 SaveConsoleBuffer(
1767     ConsoleBuffer *cb)
1768 {
1769     DWORD NumCells;
1770     COORD BufferCoord;
1771     SMALL_RECT ReadRegion;
1772     WORD Y, Y_incr;
1773 
1774     if (cb == NULL)
1775 	return FALSE;
1776 
1777     if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
1778     {
1779 	cb->IsValid = FALSE;
1780 	return FALSE;
1781     }
1782     cb->IsValid = TRUE;
1783 
1784     /*
1785      * Allocate a buffer large enough to hold the entire console screen
1786      * buffer.  If this ConsoleBuffer structure has already been initialized
1787      * with a buffer of the correct size, then just use that one.
1788      */
1789     if (!cb->IsValid || cb->Buffer == NULL ||
1790 	    cb->BufferSize.X != cb->Info.dwSize.X ||
1791 	    cb->BufferSize.Y != cb->Info.dwSize.Y)
1792     {
1793 	cb->BufferSize.X = cb->Info.dwSize.X;
1794 	cb->BufferSize.Y = cb->Info.dwSize.Y;
1795 	NumCells = cb->BufferSize.X * cb->BufferSize.Y;
1796 	if (cb->Buffer != NULL)
1797 	    vim_free(cb->Buffer);
1798 	cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
1799 	if (cb->Buffer == NULL)
1800 	    return FALSE;
1801     }
1802 
1803     /*
1804      * We will now copy the console screen buffer into our buffer.
1805      * ReadConsoleOutput() seems to be limited as far as how much you
1806      * can read at a time.  Empirically, this number seems to be about
1807      * 12000 cells (rows * columns).  Start at position (0, 0) and copy
1808      * in chunks until it is all copied.  The chunks will all have the
1809      * same horizontal characteristics, so initialize them now.  The
1810      * height of each chunk will be (12000 / width).
1811      */
1812     BufferCoord.X = 0;
1813     ReadRegion.Left = 0;
1814     ReadRegion.Right = cb->Info.dwSize.X - 1;
1815     Y_incr = 12000 / cb->Info.dwSize.X;
1816     for (Y = 0; Y < cb->BufferSize.Y; Y += Y_incr)
1817     {
1818 	/*
1819 	 * Read into position (0, Y) in our buffer.
1820 	 */
1821 	BufferCoord.Y = Y;
1822 	/*
1823 	 * Read the region whose top left corner is (0, Y) and whose bottom
1824 	 * right corner is (width - 1, Y + Y_incr - 1).  This should define
1825 	 * a region of size width by Y_incr.  Don't worry if this region is
1826 	 * too large for the remaining buffer; it will be cropped.
1827 	 */
1828 	ReadRegion.Top = Y;
1829 	ReadRegion.Bottom = Y + Y_incr - 1;
1830 	if (!ReadConsoleOutput(g_hConOut,	/* output handle */
1831 		cb->Buffer,			/* our buffer */
1832 		cb->BufferSize,			/* dimensions of our buffer */
1833 		BufferCoord,			/* offset in our buffer */
1834 		&ReadRegion))			/* region to save */
1835 	{
1836 	    vim_free(cb->Buffer);
1837 	    cb->Buffer = NULL;
1838 	    return FALSE;
1839 	}
1840     }
1841 
1842     return TRUE;
1843 }
1844 
1845 /*
1846  * RestoreConsoleBuffer()
1847  * Description:
1848  *  Restores important information about the console buffer, including the
1849  *  actual buffer contents, if desired.  The information to restore is in
1850  *  the same format used by SaveConsoleBuffer().
1851  * Returns:
1852  *  TRUE on success
1853  */
1854     static BOOL
1855 RestoreConsoleBuffer(
1856     ConsoleBuffer *cb,
1857     BOOL RestoreScreen)
1858 {
1859     COORD BufferCoord;
1860     SMALL_RECT WriteRegion;
1861 
1862     if (cb == NULL || !cb->IsValid)
1863 	return FALSE;
1864 
1865     /*
1866      * Before restoring the buffer contents, clear the current buffer, and
1867      * restore the cursor position and window information.  Doing this now
1868      * prevents old buffer contents from "flashing" onto the screen.
1869      */
1870     if (RestoreScreen)
1871 	ClearConsoleBuffer(cb->Info.wAttributes);
1872 
1873     FitConsoleWindow(cb->Info.dwSize, TRUE);
1874     if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
1875 	return FALSE;
1876     if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
1877 	return FALSE;
1878 
1879     if (!RestoreScreen)
1880     {
1881 	/*
1882 	 * No need to restore the screen buffer contents, so we're done.
1883 	 */
1884 	return TRUE;
1885     }
1886 
1887     if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
1888 	return FALSE;
1889     if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
1890 	return FALSE;
1891 
1892     /*
1893      * Restore the screen buffer contents.
1894      */
1895     if (cb->Buffer != NULL)
1896     {
1897 	BufferCoord.X = 0;
1898 	BufferCoord.Y = 0;
1899 	WriteRegion.Left = 0;
1900 	WriteRegion.Top = 0;
1901 	WriteRegion.Right = cb->Info.dwSize.X - 1;
1902 	WriteRegion.Bottom = cb->Info.dwSize.Y - 1;
1903 	if (!WriteConsoleOutput(g_hConOut,	/* output handle */
1904 		cb->Buffer,			/* our buffer */
1905 		cb->BufferSize,			/* dimensions of our buffer */
1906 		BufferCoord,			/* offset in our buffer */
1907 		&WriteRegion))			/* region to restore */
1908 	{
1909 	    return FALSE;
1910 	}
1911     }
1912 
1913     return TRUE;
1914 }
1915 
1916 #ifdef FEAT_RESTORE_ORIG_SCREEN
1917 static ConsoleBuffer g_cbOrig = { 0 };
1918 #endif
1919 static ConsoleBuffer g_cbNonTermcap = { 0 };
1920 static ConsoleBuffer g_cbTermcap = { 0 };
1921 
1922 #ifdef FEAT_TITLE
1923 #ifdef __BORLANDC__
1924 typedef HWND (__stdcall *GETCONSOLEWINDOWPROC)(VOID);
1925 #else
1926 typedef WINBASEAPI HWND (WINAPI *GETCONSOLEWINDOWPROC)(VOID);
1927 #endif
1928 char g_szOrigTitle[256] = { 0 };
1929 HWND g_hWnd = NULL;	/* also used in os_mswin.c */
1930 static HICON g_hOrigIconSmall = NULL;
1931 static HICON g_hOrigIcon = NULL;
1932 static HICON g_hVimIcon = NULL;
1933 static BOOL g_fCanChangeIcon = FALSE;
1934 
1935 /* ICON* are not defined in VC++ 4.0 */
1936 #ifndef ICON_SMALL
1937 #define ICON_SMALL 0
1938 #endif
1939 #ifndef ICON_BIG
1940 #define ICON_BIG 1
1941 #endif
1942 /*
1943  * GetConsoleIcon()
1944  * Description:
1945  *  Attempts to retrieve the small icon and/or the big icon currently in
1946  *  use by a given window.
1947  * Returns:
1948  *  TRUE on success
1949  */
1950     static BOOL
1951 GetConsoleIcon(
1952     HWND hWnd,
1953     HICON *phIconSmall,
1954     HICON *phIcon)
1955 {
1956     if (hWnd == NULL)
1957 	return FALSE;
1958 
1959     if (phIconSmall != NULL)
1960     {
1961 	*phIconSmall = (HICON) SendMessage(hWnd, WM_GETICON,
1962 			    (WPARAM) ICON_SMALL, (LPARAM) 0);
1963     }
1964     if (phIcon != NULL)
1965     {
1966 	*phIcon = (HICON) SendMessage(hWnd, WM_GETICON,
1967 			    (WPARAM) ICON_BIG, (LPARAM) 0);
1968     }
1969     return TRUE;
1970 }
1971 
1972 /*
1973  * SetConsoleIcon()
1974  * Description:
1975  *  Attempts to change the small icon and/or the big icon currently in
1976  *  use by a given window.
1977  * Returns:
1978  *  TRUE on success
1979  */
1980     static BOOL
1981 SetConsoleIcon(
1982     HWND hWnd,
1983     HICON hIconSmall,
1984     HICON hIcon)
1985 {
1986     HICON hPrevIconSmall;
1987     HICON hPrevIcon;
1988 
1989     if (hWnd == NULL)
1990 	return FALSE;
1991 
1992     if (hIconSmall != NULL)
1993     {
1994 	hPrevIconSmall = (HICON) SendMessage(hWnd, WM_SETICON,
1995 			    (WPARAM) ICON_SMALL, (LPARAM) hIconSmall);
1996     }
1997     if (hIcon != NULL)
1998     {
1999 	hPrevIcon = (HICON) SendMessage(hWnd, WM_SETICON,
2000 			    (WPARAM) ICON_BIG, (LPARAM) hIcon);
2001     }
2002     return TRUE;
2003 }
2004 
2005 /*
2006  * SaveConsoleTitleAndIcon()
2007  * Description:
2008  *  Saves the current console window title in g_szOrigTitle, for later
2009  *  restoration.  Also, attempts to obtain a handle to the console window,
2010  *  and use it to save the small and big icons currently in use by the
2011  *  console window.  This is not always possible on some versions of Windows;
2012  *  nor is it possible when running Vim remotely using Telnet (since the
2013  *  console window the user sees is owned by a remote process).
2014  */
2015     static void
2016 SaveConsoleTitleAndIcon(void)
2017 {
2018     GETCONSOLEWINDOWPROC GetConsoleWindowProc;
2019 
2020     /* Save the original title. */
2021     if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle)))
2022 	return;
2023 
2024     /*
2025      * Obtain a handle to the console window using GetConsoleWindow() from
2026      * KERNEL32.DLL; we need to handle in order to change the window icon.
2027      * This function only exists on NT-based Windows, starting with Windows
2028      * 2000.  On older operating systems, we can't change the window icon
2029      * anyway.
2030      */
2031     if ((GetConsoleWindowProc = (GETCONSOLEWINDOWPROC)
2032 	    GetProcAddress(GetModuleHandle("KERNEL32.DLL"),
2033 		    "GetConsoleWindow")) != NULL)
2034     {
2035 	g_hWnd = (*GetConsoleWindowProc)();
2036     }
2037     if (g_hWnd == NULL)
2038 	return;
2039 
2040     /* Save the original console window icon. */
2041     GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon);
2042     if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL)
2043 	return;
2044 
2045     /* Extract the first icon contained in the Vim executable. */
2046     g_hVimIcon = ExtractIcon(NULL, exe_name, 0);
2047     if (g_hVimIcon != NULL)
2048 	g_fCanChangeIcon = TRUE;
2049 }
2050 #endif
2051 
2052 static int g_fWindInitCalled = FALSE;
2053 static int g_fTermcapMode = FALSE;
2054 static CONSOLE_CURSOR_INFO g_cci;
2055 static DWORD g_cmodein = 0;
2056 static DWORD g_cmodeout = 0;
2057 
2058 /*
2059  * non-GUI version of mch_init().
2060  */
2061     void
2062 mch_init()
2063 {
2064 #ifndef FEAT_RESTORE_ORIG_SCREEN
2065     CONSOLE_SCREEN_BUFFER_INFO csbi;
2066 #endif
2067 #ifndef __MINGW32__
2068     extern int _fmode;
2069 #endif
2070 
2071     /* Let critical errors result in a failure, not in a dialog box.  Required
2072      * for the timestamp test to work on removed floppies. */
2073     SetErrorMode(SEM_FAILCRITICALERRORS);
2074 
2075     _fmode = O_BINARY;		/* we do our own CR-LF translation */
2076     out_flush();
2077 
2078     /* Obtain handles for the standard Console I/O devices */
2079     if (read_cmd_fd == 0)
2080 	g_hConIn =  GetStdHandle(STD_INPUT_HANDLE);
2081     else
2082 	create_conin();
2083     g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
2084 
2085 #ifdef FEAT_RESTORE_ORIG_SCREEN
2086     /* Save the initial console buffer for later restoration */
2087     SaveConsoleBuffer(&g_cbOrig);
2088     g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
2089 #else
2090     /* Get current text attributes */
2091     GetConsoleScreenBufferInfo(g_hConOut, &csbi);
2092     g_attrCurrent = g_attrDefault = csbi.wAttributes;
2093 #endif
2094     if (cterm_normal_fg_color == 0)
2095 	cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
2096     if (cterm_normal_bg_color == 0)
2097 	cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
2098 
2099     /* set termcap codes to current text attributes */
2100     update_tcap(g_attrCurrent);
2101 
2102     GetConsoleCursorInfo(g_hConOut, &g_cci);
2103     GetConsoleMode(g_hConIn,  &g_cmodein);
2104     GetConsoleMode(g_hConOut, &g_cmodeout);
2105 
2106 #ifdef FEAT_TITLE
2107     SaveConsoleTitleAndIcon();
2108     /*
2109      * Set both the small and big icons of the console window to Vim's icon.
2110      * Note that Vim presently only has one size of icon (32x32), but it
2111      * automatically gets scaled down to 16x16 when setting the small icon.
2112      */
2113     if (g_fCanChangeIcon)
2114 	SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon);
2115 #endif
2116 
2117     ui_get_shellsize();
2118 
2119 #ifdef MCH_WRITE_DUMP
2120     fdDump = fopen("dump", "wt");
2121 
2122     if (fdDump)
2123     {
2124 	time_t t;
2125 
2126 	time(&t);
2127 	fputs(ctime(&t), fdDump);
2128 	fflush(fdDump);
2129     }
2130 #endif
2131 
2132     g_fWindInitCalled = TRUE;
2133 
2134 #ifdef FEAT_MOUSE
2135     g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
2136 #endif
2137 
2138 #ifdef FEAT_CLIPBOARD
2139     clip_init(TRUE);
2140 
2141     /*
2142      * Vim's own clipboard format recognises whether the text is char, line, or
2143      * rectangular block.  Only useful for copying between two Vims.
2144      * "VimClipboard" was used for previous versions, using the first
2145      * character to specify MCHAR, MLINE or MBLOCK.
2146      */
2147     clip_star.format = RegisterClipboardFormat("VimClipboard2");
2148     clip_star.format_raw = RegisterClipboardFormat("VimRawBytes");
2149 #endif
2150 
2151     /* This will be NULL on anything but NT 4.0 */
2152     s_pfnGetConsoleKeyboardLayoutName =
2153 	(PFNGCKLN) GetProcAddress(GetModuleHandle("kernel32.dll"),
2154 				  "GetConsoleKeyboardLayoutNameA");
2155 }
2156 
2157 /*
2158  * non-GUI version of mch_exit().
2159  * Shut down and exit with status `r'
2160  * Careful: mch_exit() may be called before mch_init()!
2161  */
2162     void
2163 mch_exit(int r)
2164 {
2165     stoptermcap();
2166 
2167     if (g_fWindInitCalled)
2168 	settmode(TMODE_COOK);
2169 
2170     ml_close_all(TRUE);		/* remove all memfiles */
2171 
2172     if (g_fWindInitCalled)
2173     {
2174 #ifdef FEAT_TITLE
2175 	mch_restore_title(3);
2176 	/*
2177 	 * Restore both the small and big icons of the console window to
2178 	 * what they were at startup.  Don't do this when the window is
2179 	 * closed, Vim would hang here.
2180 	 */
2181 	if (g_fCanChangeIcon && !g_fForceExit)
2182 	    SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon);
2183 #endif
2184 
2185 #ifdef MCH_WRITE_DUMP
2186 	if (fdDump)
2187 	{
2188 	    time_t t;
2189 
2190 	    time(&t);
2191 	    fputs(ctime(&t), fdDump);
2192 	    fclose(fdDump);
2193 	}
2194 	fdDump = NULL;
2195 #endif
2196     }
2197 
2198     SetConsoleCursorInfo(g_hConOut, &g_cci);
2199     SetConsoleMode(g_hConIn,  g_cmodein);
2200     SetConsoleMode(g_hConOut, g_cmodeout);
2201 
2202 #ifdef DYNAMIC_GETTEXT
2203     dyn_libintl_end();
2204 #endif
2205 
2206     exit(r);
2207 }
2208 #endif /* !FEAT_GUI_W32 */
2209 
2210 /*
2211  * Do we have an interactive window?
2212  */
2213     int
2214 mch_check_win(
2215     int argc,
2216     char **argv)
2217 {
2218     get_exe_name();
2219 
2220 #ifdef FEAT_GUI_W32
2221     return OK;	    /* GUI always has a tty */
2222 #else
2223     if (isatty(1))
2224 	return OK;
2225     return FAIL;
2226 #endif
2227 }
2228 
2229 
2230 /*
2231  * fname_case(): Set the case of the file name, if it already exists.
2232  * When "len" is > 0, also expand short to long filenames.
2233  */
2234     void
2235 fname_case(
2236     char_u	*name,
2237     int		len)
2238 {
2239     char		szTrueName[_MAX_PATH + 2];
2240     char		*ptrue, *ptruePrev;
2241     char		*porig, *porigPrev;
2242     int			flen;
2243     WIN32_FIND_DATA	fb;
2244     HANDLE		hFind;
2245     int			c;
2246 
2247     flen = (name != NULL) ? (int)STRLEN(name) : 0;
2248     if (flen == 0 || flen > _MAX_PATH)
2249 	return;
2250 
2251     slash_adjust(name);
2252 
2253     /* Build the new name in szTrueName[] one component at a time. */
2254     porig = name;
2255     ptrue = szTrueName;
2256 
2257     if (isalpha(porig[0]) && porig[1] == ':')
2258     {
2259 	/* copy leading drive letter */
2260 	*ptrue++ = *porig++;
2261 	*ptrue++ = *porig++;
2262 	*ptrue = NUL;	    /* in case nothing follows */
2263     }
2264 
2265     while (*porig != NUL)
2266     {
2267 	/* copy \ characters */
2268 	while (*porig == psepc)
2269 	    *ptrue++ = *porig++;
2270 
2271 	ptruePrev = ptrue;
2272 	porigPrev = porig;
2273 	while (*porig != NUL && *porig != psepc)
2274 	{
2275 #ifdef FEAT_MBYTE
2276 	    int l;
2277 
2278 	    if (enc_dbcs)
2279 	    {
2280 		l = (*mb_ptr2len_check)(porig);
2281 		while (--l >= 0)
2282 		    *ptrue++ = *porig++;
2283 	    }
2284 	    else
2285 #endif
2286 		*ptrue++ = *porig++;
2287 	}
2288 	*ptrue = NUL;
2289 
2290 	/* Skip "", "." and "..". */
2291 	if (ptrue > ptruePrev
2292 		&& (ptruePrev[0] != '.'
2293 		    || (ptruePrev[1] != NUL
2294 			&& (ptruePrev[1] != '.' || ptruePrev[2] != NUL)))
2295 		&& (hFind = FindFirstFile(szTrueName, &fb))
2296 						      != INVALID_HANDLE_VALUE)
2297 	{
2298 	    c = *porig;
2299 	    *porig = NUL;
2300 
2301 	    /* Only use the match when it's the same name (ignoring case) or
2302 	     * expansion is allowed and there is a match with the short name
2303 	     * and there is enough room. */
2304 	    if (_stricoll(porigPrev, fb.cFileName) == 0
2305 		    || (len > 0
2306 			&& (_stricoll(porigPrev, fb.cAlternateFileName) == 0
2307 			    && (int)(ptruePrev - szTrueName)
2308 					   + (int)strlen(fb.cFileName) < len)))
2309 	    {
2310 		STRCPY(ptruePrev, fb.cFileName);
2311 
2312 		/* Look for exact match and prefer it if found.  Must be a
2313 		 * long name, otherwise there would be only one match. */
2314 		while (FindNextFile(hFind, &fb))
2315 		{
2316 		    if (*fb.cAlternateFileName != NUL
2317 			    && (strcoll(porigPrev, fb.cFileName) == 0
2318 				|| (len > 0
2319 				    && (_stricoll(porigPrev,
2320 						   fb.cAlternateFileName) == 0
2321 				    && (int)(ptruePrev - szTrueName)
2322 					 + (int)strlen(fb.cFileName) < len))))
2323 		    {
2324 			STRCPY(ptruePrev, fb.cFileName);
2325 			break;
2326 		    }
2327 		}
2328 	    }
2329 	    FindClose(hFind);
2330 	    *porig = c;
2331 	    ptrue = ptruePrev + strlen(ptruePrev);
2332 	}
2333     }
2334 
2335     STRCPY(name, szTrueName);
2336 }
2337 
2338 
2339 /*
2340  * Insert user name in s[len].
2341  */
2342     int
2343 mch_get_user_name(
2344     char_u *s,
2345     int len)
2346 {
2347     char szUserName[MAX_COMPUTERNAME_LENGTH + 1];
2348     DWORD cch = sizeof szUserName;
2349 
2350     if (GetUserName(szUserName, &cch))
2351     {
2352 	STRNCPY(s, szUserName, len);
2353 	return OK;
2354     }
2355     s[0] = NUL;
2356     return FAIL;
2357 }
2358 
2359 
2360 /*
2361  * Insert host name in s[len].
2362  */
2363     void
2364 mch_get_host_name(
2365     char_u	*s,
2366     int		len)
2367 {
2368     DWORD cch = len;
2369 
2370     if (!GetComputerName(s, &cch))
2371     {
2372 	STRNCPY(s, "PC (Win32 Vim)", len);
2373 	s[len - 1] = NUL;	/* make sure it's terminated */
2374     }
2375 }
2376 
2377 
2378 /*
2379  * return process ID
2380  */
2381     long
2382 mch_get_pid()
2383 {
2384     return (long)GetCurrentProcessId();
2385 }
2386 
2387 
2388 /*
2389  * Get name of current directory into buffer 'buf' of length 'len' bytes.
2390  * Return OK for success, FAIL for failure.
2391  */
2392     int
2393 mch_dirname(
2394     char_u	*buf,
2395     int		len)
2396 {
2397     /*
2398      * Originally this was:
2399      *    return (getcwd(buf, len) != NULL ? OK : FAIL);
2400      * But the Win32s known bug list says that getcwd() doesn't work
2401      * so use the Win32 system call instead. <Negri>
2402      */
2403 #ifdef FEAT_MBYTE
2404     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2405     {
2406 	WCHAR	wbuf[_MAX_PATH + 1];
2407 
2408 	if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0)
2409 	{
2410 	    char_u  *p = ucs2_to_enc(wbuf, NULL);
2411 
2412 	    if (p != NULL)
2413 	    {
2414 		STRNCPY(buf, p, len - 1);
2415 		buf[len - 1] = NUL;
2416 		vim_free(p);
2417 		return OK;
2418 	    }
2419 	}
2420 	/* Retry with non-wide function (for Windows 98). */
2421     }
2422 #endif
2423     return (GetCurrentDirectory(len, buf) != 0 ? OK : FAIL);
2424 }
2425 
2426 /*
2427  * get file permissions for `name'
2428  * -1 : error
2429  * else FILE_ATTRIBUTE_* defined in winnt.h
2430  */
2431     long
2432 mch_getperm(
2433     char_u *name)
2434 {
2435 #ifdef FEAT_MBYTE
2436     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2437     {
2438 	WCHAR	*p = enc_to_ucs2(name, NULL);
2439 	long	n;
2440 
2441 	if (p != NULL)
2442 	{
2443 	    n = (long)GetFileAttributesW(p);
2444 	    vim_free(p);
2445 	    if (n >= 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
2446 		return n;
2447 	    /* Retry with non-wide function (for Windows 98). */
2448 	}
2449     }
2450 #endif
2451     return (long)GetFileAttributes((char *)name);
2452 }
2453 
2454 
2455 /*
2456  * set file permission for `name' to `perm'
2457  */
2458     int
2459 mch_setperm(
2460     char_u *name,
2461     long perm)
2462 {
2463     perm |= FILE_ATTRIBUTE_ARCHIVE;	/* file has changed, set archive bit */
2464 #ifdef FEAT_MBYTE
2465     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2466     {
2467 	WCHAR	*p = enc_to_ucs2(name, NULL);
2468 	long	n;
2469 
2470 	if (p != NULL)
2471 	{
2472 	    n = (long)SetFileAttributesW(p, perm);
2473 	    vim_free(p);
2474 	    if (n || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
2475 		return n ? OK : FAIL;
2476 	    /* Retry with non-wide function (for Windows 98). */
2477 	}
2478     }
2479 #endif
2480     return SetFileAttributes((char *)name, perm) ? OK : FAIL;
2481 }
2482 
2483 /*
2484  * Set hidden flag for "name".
2485  */
2486     void
2487 mch_hide(char_u *name)
2488 {
2489     int		perm;
2490 #ifdef FEAT_MBYTE
2491     WCHAR	*p = NULL;
2492 
2493     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2494 	p = enc_to_ucs2(name, NULL);
2495 #endif
2496 
2497 #ifdef FEAT_MBYTE
2498     if (p != NULL)
2499     {
2500 	perm = GetFileAttributesW(p);
2501 	if (perm < 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2502 	{
2503 	    /* Retry with non-wide function (for Windows 98). */
2504 	    vim_free(p);
2505 	    p = NULL;
2506 	}
2507     }
2508     if (p == NULL)
2509 #endif
2510 	perm = GetFileAttributes((char *)name);
2511     if (perm >= 0)
2512     {
2513 	perm |= FILE_ATTRIBUTE_HIDDEN;
2514 #ifdef FEAT_MBYTE
2515 	if (p != NULL)
2516 	{
2517 	    if (SetFileAttributesW(p, perm) == 0
2518 		    && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2519 	    {
2520 		/* Retry with non-wide function (for Windows 98). */
2521 		vim_free(p);
2522 		p = NULL;
2523 	    }
2524 	}
2525 	if (p == NULL)
2526 #endif
2527 	    SetFileAttributes((char *)name, perm);
2528     }
2529 #ifdef FEAT_MBYTE
2530     vim_free(p);
2531 #endif
2532 }
2533 
2534 /*
2535  * return TRUE if "name" is a directory
2536  * return FALSE if "name" is not a directory or upon error
2537  */
2538     int
2539 mch_isdir(char_u *name)
2540 {
2541     int f = mch_getperm(name);
2542 
2543     if (f == -1)
2544 	return FALSE;		    /* file does not exist at all */
2545 
2546     return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
2547 }
2548 
2549 /*
2550  * Return TRUE if file or directory "name" is writable (not readonly).
2551  * Strange semantics of Win32: a readonly directory is writable, but you can't
2552  * delete a file.  Let's say this means it is writable.
2553  */
2554     int
2555 mch_writable(char_u *name)
2556 {
2557     int perm = mch_getperm(name);
2558 
2559     return (perm != -1 && (!(perm & FILE_ATTRIBUTE_READONLY)
2560 				       || (perm & FILE_ATTRIBUTE_DIRECTORY)));
2561 }
2562 
2563 #if defined(FEAT_EVAL) || defined(PROTO)
2564 /*
2565  * Return 1 if "name" can be executed, 0 if not.
2566  * Return -1 if unknown.
2567  */
2568     int
2569 mch_can_exe(char_u *name)
2570 {
2571     char_u	buf[_MAX_PATH];
2572     int		len = STRLEN(name);
2573     char_u	*p;
2574 
2575     if (len >= _MAX_PATH)	/* safety check */
2576 	return FALSE;
2577 
2578     /* If there already is an extension try using the name directly.  Also do
2579      * this with a Unix-shell like 'shell'. */
2580     if (vim_strchr(gettail(name), '.') != NULL
2581 			       || strstr((char *)gettail(p_sh), "sh") != NULL)
2582 	if (executable_exists((char *)name))
2583 	    return TRUE;
2584 
2585     /*
2586      * Loop over all extensions in $PATHEXT.
2587      */
2588     STRNCPY(buf, name, _MAX_PATH);
2589     p = mch_getenv("PATHEXT");
2590     if (p == NULL)
2591 	p = (char_u *)".com;.exe;.bat;.cmd";
2592     while (*p)
2593     {
2594 	if (p[0] == '.' && (p[1] == NUL || p[1] == ';'))
2595 	{
2596 	    /* A single "." means no extension is added. */
2597 	    buf[len] = NUL;
2598 	    ++p;
2599 	    if (*p)
2600 		++p;
2601 	}
2602 	else
2603 	    copy_option_part(&p, buf + len, _MAX_PATH - len, ";");
2604 	if (executable_exists((char *)buf))
2605 	    return TRUE;
2606     }
2607     return FALSE;
2608 }
2609 #endif
2610 
2611 /*
2612  * Check what "name" is:
2613  * NODE_NORMAL: file or directory (or doesn't exist)
2614  * NODE_WRITABLE: writable device, socket, fifo, etc.
2615  * NODE_OTHER: non-writable things
2616  */
2617     int
2618 mch_nodetype(char_u *name)
2619 {
2620     HANDLE	hFile;
2621     int		type;
2622 
2623     hFile = CreateFile(name,		/* file name */
2624 		GENERIC_WRITE,		/* access mode */
2625 		0,			/* share mode */
2626 		NULL,			/* security descriptor */
2627 		OPEN_EXISTING,		/* creation disposition */
2628 		0,			/* file attributes */
2629 		NULL);			/* handle to template file */
2630 
2631     if (hFile == INVALID_HANDLE_VALUE)
2632 	return NODE_NORMAL;
2633 
2634     type = GetFileType(hFile);
2635     CloseHandle(hFile);
2636     if (type == FILE_TYPE_CHAR)
2637 	return NODE_WRITABLE;
2638     if (type == FILE_TYPE_DISK)
2639 	return NODE_NORMAL;
2640     return NODE_OTHER;
2641 }
2642 
2643 #ifdef HAVE_ACL
2644 struct my_acl
2645 {
2646     PSECURITY_DESCRIPTOR    pSecurityDescriptor;
2647     PSID		    pSidOwner;
2648     PSID		    pSidGroup;
2649     PACL		    pDacl;
2650     PACL		    pSacl;
2651 };
2652 #endif
2653 
2654 /*
2655  * Return a pointer to the ACL of file "fname" in allocated memory.
2656  * Return NULL if the ACL is not available for whatever reason.
2657  */
2658     vim_acl_T
2659 mch_get_acl(fname)
2660     char_u	*fname;
2661 {
2662 #ifndef HAVE_ACL
2663     return (vim_acl_T)NULL;
2664 #else
2665     struct my_acl   *p = NULL;
2666 
2667     /* This only works on Windows NT and 2000. */
2668     if (g_PlatformId == VER_PLATFORM_WIN32_NT && advapi_lib != NULL)
2669     {
2670 	p = (struct my_acl *)alloc_clear((unsigned)sizeof(struct my_acl));
2671 	if (p != NULL)
2672 	{
2673 	    if (pGetNamedSecurityInfo(
2674 			(LPTSTR)fname,		// Abstract filename
2675 			SE_FILE_OBJECT,		// File Object
2676 			// Retrieve the entire security descriptor.
2677 			OWNER_SECURITY_INFORMATION |
2678 			GROUP_SECURITY_INFORMATION |
2679 			DACL_SECURITY_INFORMATION |
2680 			SACL_SECURITY_INFORMATION,
2681 			&p->pSidOwner,		// Ownership information.
2682 			&p->pSidGroup,		// Group membership.
2683 			&p->pDacl,		// Discretionary information.
2684 			&p->pSacl,		// For auditing purposes.
2685 			&p->pSecurityDescriptor
2686 				    ) != ERROR_SUCCESS)
2687 	    {
2688 		mch_free_acl((vim_acl_T)p);
2689 		p = NULL;
2690 	    }
2691 	}
2692     }
2693 
2694     return (vim_acl_T)p;
2695 #endif
2696 }
2697 
2698 /*
2699  * Set the ACL of file "fname" to "acl" (unless it's NULL).
2700  * Errors are ignored.
2701  * This must only be called with "acl" equal to what mch_get_acl() returned.
2702  */
2703     void
2704 mch_set_acl(fname, acl)
2705     char_u	*fname;
2706     vim_acl_T	acl;
2707 {
2708 #ifdef HAVE_ACL
2709     struct my_acl   *p = (struct my_acl *)acl;
2710 
2711     if (p != NULL && advapi_lib != NULL)
2712 	(void)pSetNamedSecurityInfo(
2713 		    (LPTSTR)fname,		// Abstract filename
2714 		    SE_FILE_OBJECT,		// File Object
2715 		    // Retrieve the entire security descriptor.
2716 		    OWNER_SECURITY_INFORMATION |
2717 			GROUP_SECURITY_INFORMATION |
2718 			DACL_SECURITY_INFORMATION |
2719 			SACL_SECURITY_INFORMATION,
2720 		    p->pSidOwner,		// Ownership information.
2721 		    p->pSidGroup,		// Group membership.
2722 		    p->pDacl,			// Discretionary information.
2723 		    p->pSacl			// For auditing purposes.
2724 		    );
2725 #endif
2726 }
2727 
2728     void
2729 mch_free_acl(acl)
2730     vim_acl_T	acl;
2731 {
2732 #ifdef HAVE_ACL
2733     struct my_acl   *p = (struct my_acl *)acl;
2734 
2735     if (p != NULL)
2736     {
2737 	LocalFree(p->pSecurityDescriptor);	// Free the memory just in case
2738 	vim_free(p);
2739     }
2740 #endif
2741 }
2742 
2743 #ifndef FEAT_GUI_W32
2744 
2745 /*
2746  * handler for ctrl-break, ctrl-c interrupts, and fatal events.
2747  */
2748     static BOOL WINAPI
2749 handler_routine(
2750     DWORD dwCtrlType)
2751 {
2752     switch (dwCtrlType)
2753     {
2754     case CTRL_C_EVENT:
2755 	if (ctrl_c_interrupts)
2756 	    g_fCtrlCPressed = TRUE;
2757 	return TRUE;
2758 
2759     case CTRL_BREAK_EVENT:
2760 	g_fCBrkPressed	= TRUE;
2761 	return TRUE;
2762 
2763     /* fatal events: shut down gracefully */
2764     case CTRL_CLOSE_EVENT:
2765     case CTRL_LOGOFF_EVENT:
2766     case CTRL_SHUTDOWN_EVENT:
2767 	windgoto((int)Rows - 1, 0);
2768 	g_fForceExit = TRUE;
2769 
2770 	sprintf((char *)IObuff, _("Vim: Caught %s event\n"),
2771 		(dwCtrlType == CTRL_CLOSE_EVENT
2772 		     ? _("close")
2773 		     : dwCtrlType == CTRL_LOGOFF_EVENT
2774 			 ? _("logoff")
2775 			 : _("shutdown")));
2776 #ifdef DEBUG
2777 	OutputDebugString(IObuff);
2778 #endif
2779 
2780 	preserve_exit();	/* output IObuff, preserve files and exit */
2781 
2782 	return TRUE;		/* not reached */
2783 
2784     default:
2785 	return FALSE;
2786     }
2787 }
2788 
2789 
2790 /*
2791  * set the tty in (raw) ? "raw" : "cooked" mode
2792  */
2793     void
2794 mch_settmode(
2795     int tmode)
2796 {
2797     DWORD cmodein;
2798     DWORD cmodeout;
2799     BOOL bEnableHandler;
2800 
2801     GetConsoleMode(g_hConIn, &cmodein);
2802     GetConsoleMode(g_hConOut, &cmodeout);
2803     if (tmode == TMODE_RAW)
2804     {
2805 	cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
2806 		     ENABLE_ECHO_INPUT);
2807 #ifdef FEAT_MOUSE
2808 	if (g_fMouseActive)
2809 	    cmodein |= ENABLE_MOUSE_INPUT;
2810 #endif
2811 	cmodeout &= ~(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
2812 	bEnableHandler = TRUE;
2813     }
2814     else /* cooked */
2815     {
2816 	cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
2817 		    ENABLE_ECHO_INPUT);
2818 	cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
2819 	bEnableHandler = FALSE;
2820     }
2821     SetConsoleMode(g_hConIn, cmodein);
2822     SetConsoleMode(g_hConOut, cmodeout);
2823     SetConsoleCtrlHandler(handler_routine, bEnableHandler);
2824 
2825 #ifdef MCH_WRITE_DUMP
2826     if (fdDump)
2827     {
2828 	fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n",
2829 		tmode == TMODE_RAW ? "raw" :
2830 				    tmode == TMODE_COOK ? "cooked" : "normal",
2831 		cmodein, cmodeout);
2832 	fflush(fdDump);
2833     }
2834 #endif
2835 }
2836 
2837 
2838 /*
2839  * Get the size of the current window in `Rows' and `Columns'
2840  * Return OK when size could be determined, FAIL otherwise.
2841  */
2842     int
2843 mch_get_shellsize()
2844 {
2845     CONSOLE_SCREEN_BUFFER_INFO csbi;
2846 
2847     if (!g_fTermcapMode && g_cbTermcap.IsValid)
2848     {
2849 	/*
2850 	 * For some reason, we are trying to get the screen dimensions
2851 	 * even though we are not in termcap mode.  The 'Rows' and 'Columns'
2852 	 * variables are really intended to mean the size of Vim screen
2853 	 * while in termcap mode.
2854 	 */
2855 	Rows = g_cbTermcap.Info.dwSize.Y;
2856 	Columns = g_cbTermcap.Info.dwSize.X;
2857     }
2858     else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2859     {
2860 	Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
2861 	Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
2862     }
2863     else
2864     {
2865 	Rows = 25;
2866 	Columns = 80;
2867     }
2868     return OK;
2869 }
2870 
2871 /*
2872  * Set a console window to `xSize' * `ySize'
2873  */
2874     static void
2875 ResizeConBufAndWindow(
2876     HANDLE hConsole,
2877     int xSize,
2878     int ySize)
2879 {
2880     CONSOLE_SCREEN_BUFFER_INFO csbi;	/* hold current console buffer info */
2881     SMALL_RECT	    srWindowRect;	/* hold the new console size */
2882     COORD	    coordScreen;
2883 
2884 #ifdef MCH_WRITE_DUMP
2885     if (fdDump)
2886     {
2887 	fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
2888 	fflush(fdDump);
2889     }
2890 #endif
2891 
2892     /* get the largest size we can size the console window to */
2893     coordScreen = GetLargestConsoleWindowSize(hConsole);
2894 
2895     /* define the new console window size and scroll position */
2896     srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
2897     srWindowRect.Right =  (SHORT) (min(xSize, coordScreen.X) - 1);
2898     srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
2899 
2900     if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2901     {
2902 	int sx, sy;
2903 
2904 	sx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
2905 	sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
2906 	if (sy < ySize || sx < xSize)
2907 	{
2908 	    /*
2909 	     * Increasing number of lines/columns, do buffer first.
2910 	     * Use the maximal size in x and y direction.
2911 	     */
2912 	    if (sy < ySize)
2913 		coordScreen.Y = ySize;
2914 	    else
2915 		coordScreen.Y = sy;
2916 	    if (sx < xSize)
2917 		coordScreen.X = xSize;
2918 	    else
2919 		coordScreen.X = sx;
2920 	    SetConsoleScreenBufferSize(hConsole, coordScreen);
2921 	}
2922     }
2923 
2924     if (!SetConsoleWindowInfo(g_hConOut, TRUE, &srWindowRect))
2925     {
2926 #ifdef MCH_WRITE_DUMP
2927 	if (fdDump)
2928 	{
2929 	    fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
2930 		    GetLastError());
2931 	    fflush(fdDump);
2932 	}
2933 #endif
2934     }
2935 
2936     /* define the new console buffer size */
2937     coordScreen.X = xSize;
2938     coordScreen.Y = ySize;
2939 
2940     if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
2941     {
2942 #ifdef MCH_WRITE_DUMP
2943 	if (fdDump)
2944 	{
2945 	    fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
2946 		    GetLastError());
2947 	    fflush(fdDump);
2948 	}
2949 #endif
2950     }
2951 }
2952 
2953 
2954 /*
2955  * Set the console window to `Rows' * `Columns'
2956  */
2957     void
2958 mch_set_shellsize()
2959 {
2960     COORD coordScreen;
2961 
2962     /* Don't change window size while still starting up */
2963     if (suppress_winsize != 0)
2964     {
2965 	suppress_winsize = 2;
2966 	return;
2967     }
2968 
2969     if (term_console)
2970     {
2971 	coordScreen = GetLargestConsoleWindowSize(g_hConOut);
2972 
2973 	/* Clamp Rows and Columns to reasonable values */
2974 	if (Rows > coordScreen.Y)
2975 	    Rows = coordScreen.Y;
2976 	if (Columns > coordScreen.X)
2977 	    Columns = coordScreen.X;
2978 
2979 	ResizeConBufAndWindow(g_hConOut, Columns, Rows);
2980     }
2981 }
2982 
2983 /*
2984  * Rows and/or Columns has changed.
2985  */
2986     void
2987 mch_new_shellsize()
2988 {
2989     set_scroll_region(0, 0, Columns - 1, Rows - 1);
2990 }
2991 
2992 
2993 /*
2994  * Called when started up, to set the winsize that was delayed.
2995  */
2996     void
2997 mch_set_winsize_now()
2998 {
2999     if (suppress_winsize == 2)
3000     {
3001 	suppress_winsize = 0;
3002 	mch_set_shellsize();
3003 	shell_resized();
3004     }
3005     suppress_winsize = 0;
3006 }
3007 #endif /* FEAT_GUI_W32 */
3008 
3009 
3010 
3011 #if defined(FEAT_GUI_W32) || defined(PROTO)
3012 
3013 /*
3014  * Specialised version of system() for Win32 GUI mode.
3015  * This version proceeds as follows:
3016  *    1. Create a console window for use by the subprocess
3017  *    2. Run the subprocess (it gets the allocated console by default)
3018  *    3. Wait for the subprocess to terminate and get its exit code
3019  *    4. Prompt the user to press a key to close the console window
3020  */
3021     static int
3022 mch_system(char *cmd, int options)
3023 {
3024     STARTUPINFO		si;
3025     PROCESS_INFORMATION pi;
3026     DWORD		ret = 0;
3027     HWND		hwnd = GetFocus();
3028 
3029     si.cb = sizeof(si);
3030     si.lpReserved = NULL;
3031     si.lpDesktop = NULL;
3032     si.lpTitle = NULL;
3033     si.dwFlags = STARTF_USESHOWWINDOW;
3034     /*
3035      * It's nicer to run a filter command in a minimized window, but in
3036      * Windows 95 this makes the command MUCH slower.  We can't do it under
3037      * Win32s either as it stops the synchronous spawn workaround working.
3038      */
3039     if ((options & SHELL_DOOUT) && !mch_windows95() && !gui_is_win32s())
3040 	si.wShowWindow = SW_SHOWMINIMIZED;
3041     else
3042 	si.wShowWindow = SW_SHOWNORMAL;
3043     si.cbReserved2 = 0;
3044     si.lpReserved2 = NULL;
3045 
3046     /* There is a strange error on Windows 95 when using "c:\\command.com".
3047      * When the "c:\\" is left out it works OK...? */
3048     if (mch_windows95()
3049 	    && (STRNICMP(cmd, "c:/command.com", 14) == 0
3050 		|| STRNICMP(cmd, "c:\\command.com", 14) == 0))
3051 	cmd += 3;
3052 
3053     /* Now, run the command */
3054     CreateProcess(NULL,			/* Executable name */
3055 		  cmd,			/* Command to execute */
3056 		  NULL,			/* Process security attributes */
3057 		  NULL,			/* Thread security attributes */
3058 		  FALSE,		/* Inherit handles */
3059 		  CREATE_DEFAULT_ERROR_MODE |	/* Creation flags */
3060 			CREATE_NEW_CONSOLE,
3061 		  NULL,			/* Environment */
3062 		  NULL,			/* Current directory */
3063 		  &si,			/* Startup information */
3064 		  &pi);			/* Process information */
3065 
3066 
3067     /* Wait for the command to terminate before continuing */
3068     if (g_PlatformId != VER_PLATFORM_WIN32s)
3069     {
3070 #ifdef FEAT_GUI
3071 	int	    delay = 1;
3072 
3073 	/* Keep updating the window while waiting for the shell to finish. */
3074 	for (;;)
3075 	{
3076 	    MSG	msg;
3077 
3078 	    if (PeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
3079 	    {
3080 		TranslateMessage(&msg);
3081 		DispatchMessage(&msg);
3082 	    }
3083 	    if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
3084 		break;
3085 
3086 	    /* We start waiting for a very short time and then increase it, so
3087 	     * that we respond quickly when the process is quick, and don't
3088 	     * consume too much overhead when it's slow. */
3089 	    if (delay < 50)
3090 		delay += 10;
3091 	}
3092 #else
3093 	WaitForSingleObject(pi.hProcess, INFINITE);
3094 #endif
3095 
3096 	/* Get the command exit code */
3097 	GetExitCodeProcess(pi.hProcess, &ret);
3098     }
3099     else
3100     {
3101 	/*
3102 	 * This ugly code is the only quick way of performing
3103 	 * a synchronous spawn under Win32s. Yuk.
3104 	 */
3105 	num_windows = 0;
3106 	EnumWindows(win32ssynch_cb, 0);
3107 	old_num_windows = num_windows;
3108 	do
3109 	{
3110 	    Sleep(1000);
3111 	    num_windows = 0;
3112 	    EnumWindows(win32ssynch_cb, 0);
3113 	} while (num_windows == old_num_windows);
3114 	ret = 0;
3115     }
3116 
3117     /* Close the handles to the subprocess, so that it goes away */
3118     CloseHandle(pi.hThread);
3119     CloseHandle(pi.hProcess);
3120 
3121     /* Try to get input focus back.  Doesn't always work though. */
3122     PostMessage(hwnd, WM_SETFOCUS, 0, 0);
3123 
3124     return ret;
3125 }
3126 #else
3127 
3128 # define mch_system(c, o) system(c)
3129 
3130 #endif
3131 
3132 /*
3133  * Either execute a command by calling the shell or start a new shell
3134  */
3135     int
3136 mch_call_shell(
3137     char_u *cmd,
3138     int options)	/* SHELL_*, see vim.h */
3139 {
3140     int		x = 0;
3141     int		tmode = cur_tmode;
3142 #ifdef FEAT_TITLE
3143     char szShellTitle[512];
3144 
3145     /* Change the title to reflect that we are in a subshell. */
3146     if (GetConsoleTitle(szShellTitle, sizeof(szShellTitle) - 4) > 0)
3147     {
3148 	if (cmd == NULL)
3149 	    strcat(szShellTitle, " :sh");
3150 	else
3151 	{
3152 	    strcat(szShellTitle, " - !");
3153 	    if ((strlen(szShellTitle) + strlen(cmd) < sizeof(szShellTitle)))
3154 		strcat(szShellTitle, cmd);
3155 	}
3156 	mch_settitle(szShellTitle, NULL);
3157     }
3158 #endif
3159 
3160     out_flush();
3161 
3162 #ifdef MCH_WRITE_DUMP
3163     if (fdDump)
3164     {
3165 	fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
3166 	fflush(fdDump);
3167     }
3168 #endif
3169 
3170     /*
3171      * Catch all deadly signals while running the external command, because a
3172      * CTRL-C, Ctrl-Break or illegal instruction  might otherwise kill us.
3173      */
3174     signal(SIGINT, SIG_IGN);
3175 #if defined(__GNUC__) && !defined(__MINGW32__)
3176     signal(SIGKILL, SIG_IGN);
3177 #else
3178     signal(SIGBREAK, SIG_IGN);
3179 #endif
3180     signal(SIGILL, SIG_IGN);
3181     signal(SIGFPE, SIG_IGN);
3182     signal(SIGSEGV, SIG_IGN);
3183     signal(SIGTERM, SIG_IGN);
3184     signal(SIGABRT, SIG_IGN);
3185 
3186     if (options & SHELL_COOKED)
3187 	settmode(TMODE_COOK);	/* set to normal mode */
3188 
3189     if (cmd == NULL)
3190     {
3191 	x = mch_system(p_sh, options);
3192     }
3193     else
3194     {
3195 	/* we use "command" or "cmd" to start the shell; slow but easy */
3196 	char_u *newcmd;
3197 
3198 	newcmd = lalloc((long_u) (
3199 #ifdef FEAT_GUI_W32
3200 		STRLEN(vimrun_path) +
3201 #endif
3202 		STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10), TRUE);
3203 	if (newcmd != NULL)
3204 	{
3205 	    char_u *cmdbase = (*cmd == '"' ? cmd + 1 : cmd);
3206 
3207 	    if ((STRNICMP(cmdbase, "start", 5) == 0) && vim_iswhite(cmdbase[5]))
3208 	    {
3209 		STARTUPINFO		si;
3210 		PROCESS_INFORMATION	pi;
3211 
3212 		si.cb = sizeof(si);
3213 		si.lpReserved = NULL;
3214 		si.lpDesktop = NULL;
3215 		si.lpTitle = NULL;
3216 		si.dwFlags = 0;
3217 		si.cbReserved2 = 0;
3218 		si.lpReserved2 = NULL;
3219 
3220 		cmdbase = skipwhite(cmdbase + 5);
3221 		if ((STRNICMP(cmdbase, "/min", 4) == 0)
3222 			&& vim_iswhite(cmdbase[4]))
3223 		{
3224 		    cmdbase = skipwhite(cmdbase + 4);
3225 		    si.dwFlags = STARTF_USESHOWWINDOW;
3226 		    si.wShowWindow = SW_SHOWMINNOACTIVE;
3227 		}
3228 
3229 		/* When the command is in double quotes, but 'shellxquote' is
3230 		 * empty, keep the double quotes around the command.
3231 		 * Otherwise remove the double quotes, they aren't needed
3232 		 * here, because we don't use a shell to run the command. */
3233 		if (*cmd == '"' && *p_sxq == NUL)
3234 		{
3235 		    newcmd[0] = '"';
3236 		    STRCPY(newcmd + 1, cmdbase);
3237 		}
3238 		else
3239 		{
3240 		    STRCPY(newcmd, cmdbase);
3241 		    if (*cmd == '"' && *newcmd != NUL)
3242 			newcmd[STRLEN(newcmd) - 1] = NUL;
3243 		}
3244 
3245 		/*
3246 		 * Now, start the command as a process, so that it doesn't
3247 		 * inherit our handles which causes unpleasant dangling swap
3248 		 * files if we exit before the spawned process
3249 		 */
3250 		if (CreateProcess (NULL,	// Executable name
3251 			newcmd,			// Command to execute
3252 			NULL,			// Process security attributes
3253 			NULL,			// Thread security attributes
3254 			FALSE,			// Inherit handles
3255 			CREATE_NEW_CONSOLE,	// Creation flags
3256 			NULL,			// Environment
3257 			NULL,			// Current directory
3258 			&si,			// Startup information
3259 			&pi))			// Process information
3260 		    x = 0;
3261 		else
3262 		{
3263 		    x = -1;
3264 #ifdef FEAT_GUI_W32
3265 		    EMSG(_("E371: Command not found"));
3266 #endif
3267 		}
3268 		/* Close the handles to the subprocess, so that it goes away */
3269 		CloseHandle(pi.hThread);
3270 		CloseHandle(pi.hProcess);
3271 	    }
3272 	    else
3273 	    {
3274 #if defined(FEAT_GUI_W32)
3275 		if (need_vimrun_warning)
3276 		{
3277 		    MessageBox(NULL,
3278 			    _("VIMRUN.EXE not found in your $PATH.\n"
3279 				"External commands will not pause after completion.\n"
3280 				"See  :help win32-vimrun  for more information."),
3281 			    _("Vim Warning"),
3282 			    MB_ICONWARNING);
3283 		    need_vimrun_warning = FALSE;
3284 		}
3285 		if (!s_dont_use_vimrun)
3286 		    /* Use vimrun to execute the command.  It opens a console
3287 		     * window, which can be closed without killing Vim. */
3288 		    sprintf((char *)newcmd, "%s%s%s %s %s",
3289 			    vimrun_path,
3290 			    (msg_silent != 0 || (options & SHELL_DOOUT))
3291 								 ? "-s " : "",
3292 			    p_sh, p_shcf, cmd);
3293 		else
3294 #endif
3295 		    sprintf((char *)newcmd, "%s %s %s", p_sh, p_shcf, cmd);
3296 		x = mch_system((char *)newcmd, options);
3297 	    }
3298 	    vim_free(newcmd);
3299 	}
3300     }
3301 
3302     if (tmode == TMODE_RAW)
3303 	settmode(TMODE_RAW);	/* set to raw mode */
3304 
3305     /* Print the return value, unless "vimrun" was used. */
3306     if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent
3307 #if defined(FEAT_GUI_W32)
3308 		&& ((options & SHELL_DOOUT) || s_dont_use_vimrun)
3309 #endif
3310 	    )
3311     {
3312 	smsg(_("shell returned %d"), x);
3313 	msg_putchar('\n');
3314     }
3315 #ifdef FEAT_TITLE
3316     resettitle();
3317 #endif
3318 
3319     signal(SIGINT, SIG_DFL);
3320 #if defined(__GNUC__) && !defined(__MINGW32__)
3321     signal(SIGKILL, SIG_DFL);
3322 #else
3323     signal(SIGBREAK, SIG_DFL);
3324 #endif
3325     signal(SIGILL, SIG_DFL);
3326     signal(SIGFPE, SIG_DFL);
3327     signal(SIGSEGV, SIG_DFL);
3328     signal(SIGTERM, SIG_DFL);
3329     signal(SIGABRT, SIG_DFL);
3330 
3331     return x;
3332 }
3333 
3334 
3335 #ifndef FEAT_GUI_W32
3336 
3337 /*
3338  * Start termcap mode
3339  */
3340     static void
3341 termcap_mode_start(void)
3342 {
3343     DWORD cmodein;
3344 
3345     if (g_fTermcapMode)
3346 	return;
3347 
3348     SaveConsoleBuffer(&g_cbNonTermcap);
3349 
3350     if (g_cbTermcap.IsValid)
3351     {
3352 	/*
3353 	 * We've been in termcap mode before.  Restore certain screen
3354 	 * characteristics, including the buffer size and the window
3355 	 * size.  Since we will be redrawing the screen, we don't need
3356 	 * to restore the actual contents of the buffer.
3357 	 */
3358 	RestoreConsoleBuffer(&g_cbTermcap, FALSE);
3359 	SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow);
3360 	Rows = g_cbTermcap.Info.dwSize.Y;
3361 	Columns = g_cbTermcap.Info.dwSize.X;
3362     }
3363     else
3364     {
3365 	/*
3366 	 * This is our first time entering termcap mode.  Clear the console
3367 	 * screen buffer, and resize the buffer to match the current window
3368 	 * size.  We will use this as the size of our editing environment.
3369 	 */
3370 	ClearConsoleBuffer(g_attrCurrent);
3371 	ResizeConBufAndWindow(g_hConOut, Columns, Rows);
3372     }
3373 
3374 #ifdef FEAT_TITLE
3375     resettitle();
3376 #endif
3377 
3378     GetConsoleMode(g_hConIn, &cmodein);
3379 #ifdef FEAT_MOUSE
3380     if (g_fMouseActive)
3381 	cmodein |= ENABLE_MOUSE_INPUT;
3382     else
3383 	cmodein &= ~ENABLE_MOUSE_INPUT;
3384 #endif
3385     cmodein |= ENABLE_WINDOW_INPUT;
3386     SetConsoleMode(g_hConIn, cmodein);
3387 
3388     redraw_later_clear();
3389     g_fTermcapMode = TRUE;
3390 }
3391 
3392 
3393 /*
3394  * End termcap mode
3395  */
3396     static void
3397 termcap_mode_end(void)
3398 {
3399     DWORD cmodein;
3400     ConsoleBuffer *cb;
3401     COORD coord;
3402     DWORD dwDummy;
3403 
3404     if (!g_fTermcapMode)
3405 	return;
3406 
3407     SaveConsoleBuffer(&g_cbTermcap);
3408 
3409     GetConsoleMode(g_hConIn, &cmodein);
3410     cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
3411     SetConsoleMode(g_hConIn, cmodein);
3412 
3413 #ifdef FEAT_RESTORE_ORIG_SCREEN
3414     cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
3415 #else
3416     cb = &g_cbNonTermcap;
3417 #endif
3418     RestoreConsoleBuffer(cb, p_rs);
3419     SetConsoleCursorInfo(g_hConOut, &g_cci);
3420 
3421     if (p_rs || exiting)
3422     {
3423 	/*
3424 	 * Clear anything that happens to be on the current line.
3425 	 */
3426 	coord.X = 0;
3427 	coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1));
3428 	FillConsoleOutputCharacter(g_hConOut, ' ',
3429 		cb->Info.dwSize.X, coord, &dwDummy);
3430 	/*
3431 	 * The following is just for aesthetics.  If we are exiting without
3432 	 * restoring the screen, then we want to have a prompt string
3433 	 * appear at the bottom line.  However, the command interpreter
3434 	 * seems to always advance the cursor one line before displaying
3435 	 * the prompt string, which causes the screen to scroll.  To
3436 	 * counter this, move the cursor up one line before exiting.
3437 	 */
3438 	if (exiting && !p_rs)
3439 	    coord.Y--;
3440 	/*
3441 	 * Position the cursor at the leftmost column of the desired row.
3442 	 */
3443 	SetConsoleCursorPosition(g_hConOut, coord);
3444     }
3445 
3446     g_fTermcapMode = FALSE;
3447 }
3448 #endif /* FEAT_GUI_W32 */
3449 
3450 
3451 #ifdef FEAT_GUI_W32
3452     void
3453 mch_write(
3454     char_u  *s,
3455     int	    len)
3456 {
3457     /* never used */
3458 }
3459 
3460 #else
3461 
3462 /*
3463  * clear `n' chars, starting from `coord'
3464  */
3465     static void
3466 clear_chars(
3467     COORD coord,
3468     DWORD n)
3469 {
3470     DWORD dwDummy;
3471 
3472     FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
3473     FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy);
3474 }
3475 
3476 
3477 /*
3478  * Clear the screen
3479  */
3480     static void
3481 clear_screen(void)
3482 {
3483     g_coord.X = g_coord.Y = 0;
3484     clear_chars(g_coord, Rows * Columns);
3485 }
3486 
3487 
3488 /*
3489  * Clear to end of display
3490  */
3491     static void
3492 clear_to_end_of_display(void)
3493 {
3494     clear_chars(g_coord, (Rows - g_coord.Y - 1)
3495 					   * Columns + (Columns - g_coord.X));
3496 }
3497 
3498 
3499 /*
3500  * Clear to end of line
3501  */
3502     static void
3503 clear_to_end_of_line(void)
3504 {
3505     clear_chars(g_coord, Columns - g_coord.X);
3506 }
3507 
3508 
3509 /*
3510  * Scroll the scroll region up by `cLines' lines
3511  */
3512     static void
3513 scroll(
3514     unsigned cLines)
3515 {
3516     COORD oldcoord = g_coord;
3517 
3518     gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
3519     delete_lines(cLines);
3520 
3521     g_coord = oldcoord;
3522 }
3523 
3524 
3525 /*
3526  * Set the scroll region
3527  */
3528     static void
3529 set_scroll_region(
3530     unsigned left,
3531     unsigned top,
3532     unsigned right,
3533     unsigned bottom)
3534 {
3535     if (left >= right
3536 	    || top >= bottom
3537 	    || right > (unsigned) Columns - 1
3538 	    || bottom > (unsigned) Rows - 1)
3539 	return;
3540 
3541     g_srScrollRegion.Left =   left;
3542     g_srScrollRegion.Top =    top;
3543     g_srScrollRegion.Right =  right;
3544     g_srScrollRegion.Bottom = bottom;
3545 }
3546 
3547 
3548 /*
3549  * Insert `cLines' lines at the current cursor position
3550  */
3551     static void
3552 insert_lines(
3553     unsigned cLines)
3554 {
3555     SMALL_RECT	    source;
3556     COORD	    dest;
3557     CHAR_INFO	    fill;
3558 
3559     dest.X = 0;
3560     dest.Y = g_coord.Y + cLines;
3561 
3562     source.Left   = 0;
3563     source.Top	  = g_coord.Y;
3564     source.Right  = g_srScrollRegion.Right;
3565     source.Bottom = g_srScrollRegion.Bottom - cLines;
3566 
3567     fill.Char.AsciiChar = ' ';
3568     fill.Attributes = g_attrCurrent;
3569 
3570     ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
3571 
3572     /* Here we have to deal with a win32 console flake: If the scroll
3573      * region looks like abc and we scroll c to a and fill with d we get
3574      * cbd... if we scroll block c one line at a time to a, we get cdd...
3575      * vim expects cdd consistently... So we have to deal with that
3576      * here... (this also occurs scrolling the same way in the other
3577      * direction).  */
3578 
3579     if (source.Bottom < dest.Y)
3580     {
3581 	COORD coord;
3582 
3583 	coord.X = 0;
3584 	coord.Y = source.Bottom;
3585 	clear_chars(coord, Columns * (dest.Y - source.Bottom));
3586     }
3587 }
3588 
3589 
3590 /*
3591  * Delete `cLines' lines at the current cursor position
3592  */
3593     static void
3594 delete_lines(
3595     unsigned cLines)
3596 {
3597     SMALL_RECT	    source;
3598     COORD	    dest;
3599     CHAR_INFO	    fill;
3600     int		    nb;
3601 
3602     dest.X = 0;
3603     dest.Y = g_coord.Y;
3604 
3605     source.Left   = 0;
3606     source.Top	  = g_coord.Y + cLines;
3607     source.Right  = g_srScrollRegion.Right;
3608     source.Bottom = g_srScrollRegion.Bottom;
3609 
3610     fill.Char.AsciiChar = ' ';
3611     fill.Attributes = g_attrCurrent;
3612 
3613     ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
3614 
3615     /* Here we have to deal with a win32 console flake: If the scroll
3616      * region looks like abc and we scroll c to a and fill with d we get
3617      * cbd... if we scroll block c one line at a time to a, we get cdd...
3618      * vim expects cdd consistently... So we have to deal with that
3619      * here... (this also occurs scrolling the same way in the other
3620      * direction).  */
3621 
3622     nb = dest.Y + (source.Bottom - source.Top) + 1;
3623 
3624     if (nb < source.Top)
3625     {
3626 	COORD coord;
3627 
3628 	coord.X = 0;
3629 	coord.Y = nb;
3630 	clear_chars(coord, Columns * (source.Top - nb));
3631     }
3632 }
3633 
3634 
3635 /*
3636  * Set the cursor position
3637  */
3638     static void
3639 gotoxy(
3640     unsigned x,
3641     unsigned y)
3642 {
3643     if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows)
3644 	return;
3645 
3646     /* external cursor coords are 1-based; internal are 0-based */
3647     g_coord.X = x - 1;
3648     g_coord.Y = y - 1;
3649     SetConsoleCursorPosition(g_hConOut, g_coord);
3650 }
3651 
3652 
3653 /*
3654  * Set the current text attribute = (foreground | background)
3655  * See ../doc/os_win32.txt for the numbers.
3656  */
3657     static void
3658 textattr(
3659     WORD wAttr)
3660 {
3661     g_attrCurrent = wAttr;
3662 
3663     SetConsoleTextAttribute(g_hConOut, wAttr);
3664 }
3665 
3666 
3667     static void
3668 textcolor(
3669     WORD wAttr)
3670 {
3671     g_attrCurrent = (g_attrCurrent & 0xf0) + wAttr;
3672 
3673     SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
3674 }
3675 
3676 
3677     static void
3678 textbackground(
3679     WORD wAttr)
3680 {
3681     g_attrCurrent = (g_attrCurrent & 0x0f) + (wAttr << 4);
3682 
3683     SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
3684 }
3685 
3686 
3687 /*
3688  * restore the default text attribute (whatever we started with)
3689  */
3690     static void
3691 normvideo()
3692 {
3693     textattr(g_attrDefault);
3694 }
3695 
3696 
3697 static WORD g_attrPreStandout = 0;
3698 
3699 /*
3700  * Make the text standout, by brightening it
3701  */
3702     static void
3703 standout(void)
3704 {
3705     g_attrPreStandout = g_attrCurrent;
3706     textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
3707 }
3708 
3709 
3710 /*
3711  * Turn off standout mode
3712  */
3713     static void
3714 standend()
3715 {
3716     if (g_attrPreStandout)
3717     {
3718 	textattr(g_attrPreStandout);
3719 	g_attrPreStandout = 0;
3720     }
3721 }
3722 
3723 
3724 /*
3725  * Set normal fg/bg color, based on T_ME.  Called whem t_me has been set.
3726  */
3727     void
3728 mch_set_normal_colors()
3729 {
3730     char_u	*p;
3731     int		n;
3732 
3733     cterm_normal_fg_color = (g_attrDefault & 0xf) + 1;
3734     cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1;
3735     if (T_ME[0] == ESC && T_ME[1] == '|')
3736     {
3737 	p = T_ME + 2;
3738 	n = getdigits(&p);
3739 	if (*p == 'm' && n > 0)
3740 	{
3741 	    cterm_normal_fg_color = (n & 0xf) + 1;
3742 	    cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
3743 	}
3744     }
3745 }
3746 
3747 
3748 /*
3749  * visual bell: flash the screen
3750  */
3751     static void
3752 visual_bell()
3753 {
3754     COORD   coordOrigin = {0, 0};
3755     WORD    attrFlash = ~g_attrCurrent & 0xff;
3756 
3757     DWORD   dwDummy;
3758     LPWORD  oldattrs = (LPWORD)alloc(Rows * Columns * sizeof(WORD));
3759 
3760     if (oldattrs == NULL)
3761 	return;
3762     ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
3763 			       coordOrigin, &dwDummy);
3764     FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns,
3765 			       coordOrigin, &dwDummy);
3766 
3767     Sleep(15);	    /* wait for 15 msec */
3768     WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
3769 				coordOrigin, &dwDummy);
3770     vim_free(oldattrs);
3771 }
3772 
3773 
3774 /*
3775  * Make the cursor visible or invisible
3776  */
3777     static void
3778 cursor_visible(
3779     BOOL fVisible)
3780 {
3781     s_cursor_visible = fVisible;
3782 #ifdef MCH_CURSOR_SHAPE
3783     mch_update_cursor();
3784 #endif
3785 }
3786 
3787 
3788 /*
3789  * write `cchToWrite' characters in `pchBuf' to the screen
3790  * Returns the number of characters actually written (at least one).
3791  */
3792     static BOOL
3793 write_chars(
3794     LPCSTR pchBuf,
3795     DWORD  cchToWrite)
3796 {
3797     COORD coord = g_coord;
3798     DWORD written;
3799 
3800     FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cchToWrite,
3801 				coord, &written);
3802     /* When writing fails or didn't write a single character, pretend one
3803      * character was written, otherwise we get stuck. */
3804     if (WriteConsoleOutputCharacter(g_hConOut, pchBuf, cchToWrite,
3805 				coord, &written) == 0
3806 	    || written == 0)
3807 	written = 1;
3808 
3809     g_coord.X += (SHORT) written;
3810 
3811     while (g_coord.X > g_srScrollRegion.Right)
3812     {
3813 	g_coord.X -= (SHORT) Columns;
3814 	if (g_coord.Y < g_srScrollRegion.Bottom)
3815 	    g_coord.Y++;
3816     }
3817 
3818     gotoxy(g_coord.X + 1, g_coord.Y + 1);
3819 
3820     return written;
3821 }
3822 
3823 
3824 /*
3825  * mch_write(): write the output buffer to the screen, translating ESC
3826  * sequences into calls to console output routines.
3827  */
3828     void
3829 mch_write(
3830     char_u  *s,
3831     int	    len)
3832 {
3833     s[len] = NUL;
3834 
3835     if (!term_console)
3836     {
3837 	write(1, s, (unsigned)len);
3838 	return;
3839     }
3840 
3841     /* translate ESC | sequences into faked bios calls */
3842     while (len--)
3843     {
3844 	/* optimization: use one single write_chars for runs of text,
3845 	 * rather than once per character  It ain't curses, but it helps. */
3846 	DWORD  prefix = strcspn(s, "\n\r\b\a\033");
3847 
3848 	if (p_wd)
3849 	{
3850 	    WaitForChar(p_wd);
3851 	    if (prefix != 0)
3852 		prefix = 1;
3853 	}
3854 
3855 	if (prefix != 0)
3856 	{
3857 	    DWORD nWritten;
3858 
3859 	    nWritten = write_chars(s, prefix);
3860 #ifdef MCH_WRITE_DUMP
3861 	    if (fdDump)
3862 	    {
3863 		fputc('>', fdDump);
3864 		fwrite(s, sizeof(char_u), nWritten, fdDump);
3865 		fputs("<\n", fdDump);
3866 	    }
3867 #endif
3868 	    len -= (nWritten - 1);
3869 	    s += nWritten;
3870 	}
3871 	else if (s[0] == '\n')
3872 	{
3873 	    /* \n, newline: go to the beginning of the next line or scroll */
3874 	    if (g_coord.Y == g_srScrollRegion.Bottom)
3875 	    {
3876 		scroll(1);
3877 		gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
3878 	    }
3879 	    else
3880 	    {
3881 		gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
3882 	    }
3883 #ifdef MCH_WRITE_DUMP
3884 	    if (fdDump)
3885 		fputs("\\n\n", fdDump);
3886 #endif
3887 	    s++;
3888 	}
3889 	else if (s[0] == '\r')
3890 	{
3891 	    /* \r, carriage return: go to beginning of line */
3892 	    gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
3893 #ifdef MCH_WRITE_DUMP
3894 	    if (fdDump)
3895 		fputs("\\r\n", fdDump);
3896 #endif
3897 	    s++;
3898 	}
3899 	else if (s[0] == '\b')
3900 	{
3901 	    /* \b, backspace: move cursor one position left */
3902 	    if (g_coord.X > g_srScrollRegion.Left)
3903 		g_coord.X--;
3904 	    else if (g_coord.Y > g_srScrollRegion.Top)
3905 	    {
3906 		g_coord.X = g_srScrollRegion.Right;
3907 		g_coord.Y--;
3908 	    }
3909 	    gotoxy(g_coord.X + 1, g_coord.Y + 1);
3910 #ifdef MCH_WRITE_DUMP
3911 	    if (fdDump)
3912 		fputs("\\b\n", fdDump);
3913 #endif
3914 	    s++;
3915 	}
3916 	else if (s[0] == '\a')
3917 	{
3918 	    /* \a, bell */
3919 	    MessageBeep(0xFFFFFFFF);
3920 #ifdef MCH_WRITE_DUMP
3921 	    if (fdDump)
3922 		fputs("\\a\n", fdDump);
3923 #endif
3924 	    s++;
3925 	}
3926 	else if (s[0] == ESC && len >= 3-1 && s[1] == '|')
3927 	{
3928 #ifdef MCH_WRITE_DUMP
3929 	    char_u* old_s = s;
3930 #endif
3931 	    char_u* p;
3932 	    int arg1 = 0, arg2 = 0;
3933 
3934 	    switch (s[2])
3935 	    {
3936 	    /* one or two numeric arguments, separated by ';' */
3937 
3938 	    case '0': case '1': case '2': case '3': case '4':
3939 	    case '5': case '6': case '7': case '8': case '9':
3940 		p = s + 2;
3941 		arg1 = getdigits(&p);	    /* no check for length! */
3942 		if (p > s + len)
3943 		    break;
3944 
3945 		if (*p == ';')
3946 		{
3947 		    ++p;
3948 		    arg2 = getdigits(&p);   /* no check for length! */
3949 		    if (p > s + len)
3950 			break;
3951 
3952 		    if (*p == 'H')
3953 			gotoxy(arg2, arg1);
3954 		    else if (*p == 'r')
3955 			set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
3956 		}
3957 		else if (*p == 'A')
3958 		{
3959 		    /* move cursor up arg1 lines in same column */
3960 		    gotoxy(g_coord.X + 1,
3961 			   max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
3962 		}
3963 		else if (*p == 'C')
3964 		{
3965 		    /* move cursor right arg1 columns in same line */
3966 		    gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
3967 			   g_coord.Y + 1);
3968 		}
3969 		else if (*p == 'H')
3970 		{
3971 		    gotoxy(1, arg1);
3972 		}
3973 		else if (*p == 'L')
3974 		{
3975 		    insert_lines(arg1);
3976 		}
3977 		else if (*p == 'm')
3978 		{
3979 		    if (arg1 == 0)
3980 			normvideo();
3981 		    else
3982 			textattr((WORD) arg1);
3983 		}
3984 		else if (*p == 'f')
3985 		{
3986 		    textcolor((WORD) arg1);
3987 		}
3988 		else if (*p == 'b')
3989 		{
3990 		    textbackground((WORD) arg1);
3991 		}
3992 		else if (*p == 'M')
3993 		{
3994 		    delete_lines(arg1);
3995 		}
3996 
3997 		len -= p - s;
3998 		s = p + 1;
3999 		break;
4000 
4001 
4002 	    /* Three-character escape sequences */
4003 
4004 	    case 'A':
4005 		/* move cursor up one line in same column */
4006 		gotoxy(g_coord.X + 1,
4007 		       max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
4008 		goto got3;
4009 
4010 	    case 'B':
4011 		visual_bell();
4012 		goto got3;
4013 
4014 	    case 'C':
4015 		/* move cursor right one column in same line */
4016 		gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
4017 		       g_coord.Y + 1);
4018 		goto got3;
4019 
4020 	    case 'E':
4021 		termcap_mode_end();
4022 		goto got3;
4023 
4024 	    case 'F':
4025 		standout();
4026 		goto got3;
4027 
4028 	    case 'f':
4029 		standend();
4030 		goto got3;
4031 
4032 	    case 'H':
4033 		gotoxy(1, 1);
4034 		goto got3;
4035 
4036 	    case 'j':
4037 		clear_to_end_of_display();
4038 		goto got3;
4039 
4040 	    case 'J':
4041 		clear_screen();
4042 		goto got3;
4043 
4044 	    case 'K':
4045 		clear_to_end_of_line();
4046 		goto got3;
4047 
4048 	    case 'L':
4049 		insert_lines(1);
4050 		goto got3;
4051 
4052 	    case 'M':
4053 		delete_lines(1);
4054 		goto got3;
4055 
4056 	    case 'S':
4057 		termcap_mode_start();
4058 		goto got3;
4059 
4060 	    case 'V':
4061 		cursor_visible(TRUE);
4062 		goto got3;
4063 
4064 	    case 'v':
4065 		cursor_visible(FALSE);
4066 		goto got3;
4067 
4068 	    got3:
4069 		s += 3;
4070 		len -= 2;
4071 	    }
4072 
4073 #ifdef MCH_WRITE_DUMP
4074 	    if (fdDump)
4075 	    {
4076 		fputs("ESC | ", fdDump);
4077 		fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
4078 		fputc('\n', fdDump);
4079 	    }
4080 #endif
4081 	}
4082 	else
4083 	{
4084 	    /* Write a single character */
4085 	    DWORD nWritten;
4086 
4087 	    nWritten = write_chars(s, 1);
4088 #ifdef MCH_WRITE_DUMP
4089 	    if (fdDump)
4090 	    {
4091 		fputc('>', fdDump);
4092 		fwrite(s, sizeof(char_u), nWritten, fdDump);
4093 		fputs("<\n", fdDump);
4094 	    }
4095 #endif
4096 
4097 	    len -= (nWritten - 1);
4098 	    s += nWritten;
4099 	}
4100     }
4101 
4102 #ifdef MCH_WRITE_DUMP
4103     if (fdDump)
4104 	fflush(fdDump);
4105 #endif
4106 }
4107 
4108 #endif /* FEAT_GUI_W32 */
4109 
4110 
4111 /*
4112  * Delay for half a second.
4113  */
4114     void
4115 mch_delay(
4116     long    msec,
4117     int	    ignoreinput)
4118 {
4119 #ifdef FEAT_GUI_W32
4120     Sleep((int)msec);	    /* never wait for input */
4121 #else /* Console */
4122     if (ignoreinput)
4123 # ifdef FEAT_MZSCHEME
4124 	if (mzthreads_allowed() && p_mzq > 0 && msec > p_mzq)
4125 	{
4126 	    int towait = p_mzq;
4127 
4128 	    /* if msec is large enough, wait by portions in p_mzq */
4129 	    while (msec > 0)
4130 	    {
4131 		mzvim_check_threads();
4132 		if (msec < towait)
4133 		    towait = msec;
4134 		Sleep(towait);
4135 		msec -= towait;
4136 	    }
4137 	}
4138 	else
4139 # endif
4140 	    Sleep((int)msec);
4141     else
4142 	WaitForChar(msec);
4143 #endif
4144 }
4145 
4146 
4147 /*
4148  * this version of remove is not scared by a readonly (backup) file
4149  * Return 0 for success, -1 for failure.
4150  */
4151     int
4152 mch_remove(char_u *name)
4153 {
4154 #ifdef FEAT_MBYTE
4155     WCHAR	*wn = NULL;
4156     int		n;
4157 
4158     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4159     {
4160 	wn = enc_to_ucs2(name, NULL);
4161 	if (wn != NULL)
4162 	{
4163 	    SetFileAttributesW(wn, FILE_ATTRIBUTE_NORMAL);
4164 	    n = DeleteFileW(wn) ? 0 : -1;
4165 	    vim_free(wn);
4166 	    if (n == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4167 		return n;
4168 	    /* Retry with non-wide function (for Windows 98). */
4169 	}
4170     }
4171 #endif
4172     SetFileAttributes(name, FILE_ATTRIBUTE_NORMAL);
4173     return DeleteFile(name) ? 0 : -1;
4174 }
4175 
4176 
4177 /*
4178  * check for an "interrupt signal": CTRL-break or CTRL-C
4179  */
4180     void
4181 mch_breakcheck()
4182 {
4183 #ifndef FEAT_GUI_W32	    /* never used */
4184     if (g_fCtrlCPressed || g_fCBrkPressed)
4185     {
4186 	g_fCtrlCPressed = g_fCBrkPressed = FALSE;
4187 	got_int = TRUE;
4188     }
4189 #endif
4190 }
4191 
4192 
4193 /*
4194  * How much memory is available?
4195  * Return sum of available physical and page file memory.
4196  */
4197     long_u
4198 mch_avail_mem(
4199     int special)
4200 {
4201     MEMORYSTATUS	ms;
4202 
4203     ms.dwLength = sizeof(MEMORYSTATUS);
4204     GlobalMemoryStatus(&ms);
4205     return (long_u) (ms.dwAvailPhys + ms.dwAvailPageFile);
4206 }
4207 
4208 #ifdef FEAT_MBYTE
4209 /*
4210  * Same code as below, but with wide functions and no comments.
4211  * Return 0 for success, non-zero for failure.
4212  */
4213     int
4214 mch_wrename(WCHAR *wold, WCHAR *wnew)
4215 {
4216     WCHAR	*p;
4217     int		i;
4218     WCHAR	szTempFile[_MAX_PATH + 1];
4219     WCHAR	szNewPath[_MAX_PATH + 1];
4220     HANDLE	hf;
4221 
4222     if (!mch_windows95())
4223     {
4224 	p = wold;
4225 	for (i = 0; wold[i] != NUL; ++i)
4226 	    if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':')
4227 		    && wold[i + 1] != 0)
4228 		p = wold + i + 1;
4229 	if ((int)(wold + i - p) < 8 || p[6] != '~')
4230 	    return (MoveFileW(wold, wnew) == 0);
4231     }
4232 
4233     if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL)
4234 	return -1;
4235     *p = NUL;
4236 
4237     if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0)
4238 	return -2;
4239 
4240     if (!DeleteFileW(szTempFile))
4241 	return -3;
4242 
4243     if (!MoveFileW(wold, szTempFile))
4244 	return -4;
4245 
4246     if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW,
4247 		    FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
4248 	return -5;
4249     if (!CloseHandle(hf))
4250 	return -6;
4251 
4252     if (!MoveFileW(szTempFile, wnew))
4253     {
4254 	(void)MoveFileW(szTempFile, wold);
4255 	return -7;
4256     }
4257 
4258     DeleteFileW(szTempFile);
4259 
4260     if (!DeleteFileW(wold))
4261 	return -8;
4262 
4263     return 0;
4264 }
4265 #endif
4266 
4267 
4268 /*
4269  * mch_rename() works around a bug in rename (aka MoveFile) in
4270  * Windows 95: rename("foo.bar", "foo.bar~") will generate a
4271  * file whose short file name is "FOO.BAR" (its long file name will
4272  * be correct: "foo.bar~").  Because a file can be accessed by
4273  * either its SFN or its LFN, "foo.bar" has effectively been
4274  * renamed to "foo.bar", which is not at all what was wanted.  This
4275  * seems to happen only when renaming files with three-character
4276  * extensions by appending a suffix that does not include ".".
4277  * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
4278  *
4279  * There is another problem, which isn't really a bug but isn't right either:
4280  * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be
4281  * "abcdef~1.txt" again.  This has been reported on Windows NT 4.0 with
4282  * service pack 6.  Doesn't seem to happen on Windows 98.
4283  *
4284  * Like rename(), returns 0 upon success, non-zero upon failure.
4285  * Should probably set errno appropriately when errors occur.
4286  */
4287     int
4288 mch_rename(
4289     const char	*pszOldFile,
4290     const char	*pszNewFile)
4291 {
4292     char	szTempFile[_MAX_PATH+1];
4293     char	szNewPath[_MAX_PATH+1];
4294     char	*pszFilePart;
4295     HANDLE	hf;
4296 #ifdef FEAT_MBYTE
4297     WCHAR	*wold = NULL;
4298     WCHAR	*wnew = NULL;
4299     int		retval = -1;
4300 
4301     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4302     {
4303 	wold = enc_to_ucs2((char_u *)pszOldFile, NULL);
4304 	wnew = enc_to_ucs2((char_u *)pszNewFile, NULL);
4305 	if (wold != NULL && wnew != NULL)
4306 	    retval = mch_wrename(wold, wnew);
4307 	vim_free(wold);
4308 	vim_free(wnew);
4309 	if (retval == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4310 	    return retval;
4311 	/* Retry with non-wide function (for Windows 98). */
4312     }
4313 #endif
4314 
4315     /*
4316      * No need to play tricks if not running Windows 95, unless the file name
4317      * contains a "~" as the seventh character.
4318      */
4319     if (!mch_windows95())
4320     {
4321 	pszFilePart = (char *)gettail((char_u *)pszOldFile);
4322 	if (STRLEN(pszFilePart) < 8 || pszFilePart[6] != '~')
4323 	    return rename(pszOldFile, pszNewFile);
4324     }
4325 
4326     /* Get base path of new file name.  Undocumented feature: If pszNewFile is
4327      * a directory, no error is returned and pszFilePart will be NULL. */
4328     if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0
4329 	    || pszFilePart == NULL)
4330 	return -1;
4331     *pszFilePart = NUL;
4332 
4333     /* Get (and create) a unique temporary file name in directory of new file */
4334     if (GetTempFileName(szNewPath, "VIM", 0, szTempFile) == 0)
4335 	return -2;
4336 
4337     /* blow the temp file away */
4338     if (!DeleteFile(szTempFile))
4339 	return -3;
4340 
4341     /* rename old file to the temp file */
4342     if (!MoveFile(pszOldFile, szTempFile))
4343 	return -4;
4344 
4345     /* now create an empty file called pszOldFile; this prevents the operating
4346      * system using pszOldFile as an alias (SFN) if we're renaming within the
4347      * same directory.  For example, we're editing a file called
4348      * filename.asc.txt by its SFN, filena~1.txt.  If we rename filena~1.txt
4349      * to filena~1.txt~ (i.e., we're making a backup while writing it), the
4350      * SFN for filena~1.txt~ will be filena~1.txt, by default, which will
4351      * cause all sorts of problems later in buf_write.  So, we create an empty
4352      * file called filena~1.txt and the system will have to find some other
4353      * SFN for filena~1.txt~, such as filena~2.txt
4354      */
4355     if ((hf = CreateFile(pszOldFile, GENERIC_WRITE, 0, NULL, CREATE_NEW,
4356 		    FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
4357 	return -5;
4358     if (!CloseHandle(hf))
4359 	return -6;
4360 
4361     /* rename the temp file to the new file */
4362     if (!MoveFile(szTempFile, pszNewFile))
4363     {
4364 	/* Renaming failed.  Rename the file back to its old name, so that it
4365 	 * looks like nothing happened. */
4366 	(void)MoveFile(szTempFile, pszOldFile);
4367 
4368 	return -7;
4369     }
4370 
4371     /* Seems to be left around on Novell filesystems */
4372     DeleteFile(szTempFile);
4373 
4374     /* finally, remove the empty old file */
4375     if (!DeleteFile(pszOldFile))
4376 	return -8;
4377 
4378     return 0;	/* success */
4379 }
4380 
4381 /*
4382  * Get the default shell for the current hardware platform
4383  */
4384     char *
4385 default_shell()
4386 {
4387     char* psz = NULL;
4388 
4389     PlatformId();
4390 
4391     if (g_PlatformId == VER_PLATFORM_WIN32_NT)		/* Windows NT */
4392 	psz = "cmd.exe";
4393     else if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS) /* Windows 95 */
4394 	psz = "command.com";
4395 
4396     return psz;
4397 }
4398 
4399 /*
4400  * mch_access() extends access() to do more detailed check on network drives.
4401  * Returns 0 if file "n" has access rights according to "p", -1 otherwise.
4402  */
4403     int
4404 mch_access(char *n, int p)
4405 {
4406     HANDLE	hFile;
4407     DWORD	am;
4408     int		retval = -1;	    /* default: fail */
4409 #ifdef FEAT_MBYTE
4410     WCHAR	*wn = NULL;
4411 
4412     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4413 	wn = enc_to_ucs2(n, NULL);
4414 #endif
4415 
4416     if (mch_isdir(n))
4417     {
4418 	char TempName[_MAX_PATH + 16] = "";
4419 #ifdef FEAT_MBYTE
4420 	WCHAR TempNameW[_MAX_PATH + 16] = L"";
4421 #endif
4422 
4423 	if (p & R_OK)
4424 	{
4425 	    /* Read check is performed by seeing if we can do a find file on
4426 	     * the directory for any file. */
4427 #ifdef FEAT_MBYTE
4428 	    if (wn != NULL)
4429 	    {
4430 		int		    i;
4431 		WIN32_FIND_DATAW    d;
4432 
4433 		for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i)
4434 		    TempNameW[i] = wn[i];
4435 		if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/')
4436 		    TempNameW[i++] = '\\';
4437 		TempNameW[i++] = '*';
4438 		TempNameW[i++] = 0;
4439 
4440 		hFile = FindFirstFileW(TempNameW, &d);
4441 		if (hFile == INVALID_HANDLE_VALUE)
4442 		{
4443 		    if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4444 			goto getout;
4445 
4446 		    /* Retry with non-wide function (for Windows 98). */
4447 		    vim_free(wn);
4448 		    wn = NULL;
4449 		}
4450 		else
4451 		    (void)FindClose(hFile);
4452 	    }
4453 	    if (wn == NULL)
4454 #endif
4455 	    {
4456 		char		    *pch;
4457 		WIN32_FIND_DATA	    d;
4458 
4459 		STRNCPY(TempName, n, _MAX_PATH);
4460 		pch = TempName + STRLEN(TempName) - 1;
4461 		if (*pch != '\\' && *pch != '/')
4462 		    *++pch = '\\';
4463 		*++pch = '*';
4464 		*++pch = NUL;
4465 
4466 		hFile = FindFirstFile(TempName, &d);
4467 		if (hFile == INVALID_HANDLE_VALUE)
4468 		    goto getout;
4469 		(void)FindClose(hFile);
4470 	    }
4471 	}
4472 
4473 	if (p & W_OK)
4474 	{
4475 	    /* Trying to create a temporary file in the directory should catch
4476 	     * directories on read-only network shares.  However, in
4477 	     * directories whose ACL allows writes but denies deletes will end
4478 	     * up keeping the temporary file :-(. */
4479 #ifdef FEAT_MBYTE
4480 	    if (wn != NULL)
4481 	    {
4482 		if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW))
4483 		{
4484 		    if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4485 			goto getout;
4486 
4487 		    /* Retry with non-wide function (for Windows 98). */
4488 		    vim_free(wn);
4489 		    wn = NULL;
4490 		}
4491 		else
4492 		    DeleteFileW(TempNameW);
4493 	    }
4494 	    if (wn == NULL)
4495 #endif
4496 	    {
4497 		if (!GetTempFileName(n, "VIM", 0, TempName))
4498 		    goto getout;
4499 		mch_remove((char_u *)TempName);
4500 	    }
4501 	}
4502     }
4503     else
4504     {
4505 	/* Trying to open the file for the required access does ACL, read-only
4506 	 * network share, and file attribute checks.  */
4507 	am = ((p & W_OK) ? GENERIC_WRITE : 0)
4508 		| ((p & R_OK) ? GENERIC_READ : 0);
4509 #ifdef FEAT_MBYTE
4510 	if (wn != NULL)
4511 	{
4512 	    hFile = CreateFileW(wn, am, 0, NULL, OPEN_EXISTING, 0, NULL);
4513 	    if (hFile == INVALID_HANDLE_VALUE
4514 			      && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
4515 	    {
4516 		/* Retry with non-wide function (for Windows 98). */
4517 		vim_free(wn);
4518 		wn = NULL;
4519 	    }
4520 	}
4521 	if (wn == NULL)
4522 #endif
4523 	    hFile = CreateFile(n, am, 0, NULL, OPEN_EXISTING, 0, NULL);
4524 	if (hFile == INVALID_HANDLE_VALUE)
4525 	    goto getout;
4526 	CloseHandle(hFile);
4527     }
4528 
4529     retval = 0;	    /* success */
4530 getout:
4531 #ifdef FEAT_MBYTE
4532     vim_free(wn);
4533 #endif
4534     return retval;
4535 }
4536 
4537 #if defined(FEAT_MBYTE) || defined(PROTO)
4538 /*
4539  * Version of open() that may use ucs2 file name.
4540  */
4541     int
4542 mch_open(char *name, int flags, int mode)
4543 {
4544     WCHAR	*wn;
4545     int		f;
4546 
4547     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
4548 # ifdef __BORLANDC__
4549 	    /* Wide functions of Borland C 5.5 do not work on Windows 98. */
4550 	    && g_PlatformId == VER_PLATFORM_WIN32_NT
4551 # endif
4552        )
4553     {
4554 	wn = enc_to_ucs2(name, NULL);
4555 	if (wn != NULL)
4556 	{
4557 	    f = _wopen(wn, flags, mode);
4558 	    vim_free(wn);
4559 	    if (f >= 0)
4560 		return f;
4561 	    /* Retry with non-wide function (for Windows 98). Can't use
4562 	     * GetLastError() here and it's unclear what errno gets set to if
4563 	     * the _wopen() fails for missing wide functions. */
4564 	}
4565     }
4566 
4567     return open(name, flags, mode);
4568 }
4569 
4570 /*
4571  * Version of fopen() that may use ucs2 file name.
4572  */
4573     FILE *
4574 mch_fopen(char *name, char *mode)
4575 {
4576     WCHAR	*wn, *wm;
4577     FILE	*f = NULL;
4578 
4579     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
4580 # ifdef __BORLANDC__
4581 	    /* Wide functions of Borland C 5.5 do not work on Windows 98. */
4582 	    && g_PlatformId == VER_PLATFORM_WIN32_NT
4583 # endif
4584        )
4585     {
4586 	wn = enc_to_ucs2(name, NULL);
4587 	wm = enc_to_ucs2(mode, NULL);
4588 	if (wn != NULL && wm != NULL)
4589 	    f = _wfopen(wn, wm);
4590 	vim_free(wn);
4591 	vim_free(wm);
4592 	if (f != NULL)
4593 	    return f;
4594 	/* Retry with non-wide function (for Windows 98). Can't use
4595 	 * GetLastError() here and it's unclear what errno gets set to if
4596 	 * the _wfopen() fails for missing wide functions. */
4597     }
4598 
4599     return fopen(name, mode);
4600 }
4601 #endif
4602 
4603 #ifdef FEAT_MBYTE
4604 /*
4605  * SUB STREAM (aka info stream) handling:
4606  *
4607  * NTFS can have sub streams for each file.  Normal contents of file is
4608  * stored in the main stream, and extra contents (author information and
4609  * title and so on) can be stored in sub stream.  After Windows 2000, user
4610  * can access and store those informations in sub streams via explorer's
4611  * property menuitem in right click menu.  Those informations in sub streams
4612  * were lost when copying only the main stream.  So we have to copy sub
4613  * streams.
4614  *
4615  * Incomplete explanation:
4616  *	http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp
4617  * More useful info and an example:
4618  *	http://www.sysinternals.com/ntw2k/source/misc.shtml#streams
4619  */
4620 
4621 /*
4622  * Copy info stream data "substream".  Read from the file with BackupRead(sh)
4623  * and write to stream "substream" of file "to".
4624  * Errors are ignored.
4625  */
4626     static void
4627 copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len)
4628 {
4629     HANDLE  hTo;
4630     WCHAR   *to_name;
4631 
4632     to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR));
4633     wcscpy(to_name, to);
4634     wcscat(to_name, substream);
4635 
4636     hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
4637 						 FILE_ATTRIBUTE_NORMAL, NULL);
4638     if (hTo != INVALID_HANDLE_VALUE)
4639     {
4640 	long	done;
4641 	DWORD	todo;
4642 	DWORD	readcnt, written;
4643 	char	buf[4096];
4644 
4645 	/* Copy block of bytes at a time.  Abort when something goes wrong. */
4646 	for (done = 0; done < len; done += written)
4647 	{
4648 	    /* (size_t) cast for Borland C 5.5 */
4649 	    todo = (size_t)(len - done) > sizeof(buf) ? sizeof(buf)
4650 						       : (size_t)(len - done);
4651 	    if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt,
4652 						       FALSE, FALSE, context)
4653 		    || readcnt != todo
4654 		    || !WriteFile(hTo, buf, todo, &written, NULL)
4655 		    || written != todo)
4656 		break;
4657 	}
4658 	CloseHandle(hTo);
4659     }
4660 
4661     free(to_name);
4662 }
4663 
4664 /*
4665  * Copy info streams from file "from" to file "to".
4666  */
4667     static void
4668 copy_infostreams(char_u *from, char_u *to)
4669 {
4670     WCHAR		*fromw;
4671     WCHAR		*tow;
4672     HANDLE		sh;
4673     WIN32_STREAM_ID	sid;
4674     int			headersize;
4675     WCHAR		streamname[_MAX_PATH];
4676     DWORD		readcount;
4677     void		*context = NULL;
4678     DWORD		lo, hi;
4679     int			len;
4680 
4681     /* Convert the file names to wide characters. */
4682     fromw = enc_to_ucs2(from, NULL);
4683     tow = enc_to_ucs2(to, NULL);
4684     if (fromw != NULL && tow != NULL)
4685     {
4686 	/* Open the file for reading. */
4687 	sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL,
4688 			     OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
4689 	if (sh != INVALID_HANDLE_VALUE)
4690 	{
4691 	    /* Use BackupRead() to find the info streams.  Repeat until we
4692 	     * have done them all.*/
4693 	    for (;;)
4694 	    {
4695 		/* Get the header to find the length of the stream name.  If
4696 		 * the "readcount" is zero we have done all info streams. */
4697 		ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
4698 		headersize = (char *)&sid.cStreamName - (char *)&sid.dwStreamId;
4699 		if (!BackupRead(sh, (LPBYTE)&sid, headersize,
4700 					   &readcount, FALSE, FALSE, &context)
4701 			|| readcount == 0)
4702 		    break;
4703 
4704 		/* We only deal with streams that have a name.  The normal
4705 		 * file data appears to be without a name, even though docs
4706 		 * suggest it is called "::$DATA". */
4707 		if (sid.dwStreamNameSize > 0)
4708 		{
4709 		    /* Read the stream name. */
4710 		    if (!BackupRead(sh, (LPBYTE)streamname,
4711 							 sid.dwStreamNameSize,
4712 					  &readcount, FALSE, FALSE, &context))
4713 			break;
4714 
4715 		    /* Copy an info stream with a name ":anything:$DATA".
4716 		     * Skip "::$DATA", it has no stream name (examples suggest
4717 		     * it might be used for the normal file contents).
4718 		     * Note that BackupRead() counts bytes, but the name is in
4719 		     * wide characters. */
4720 		    len = readcount / sizeof(WCHAR);
4721 		    streamname[len] = 0;
4722 		    if (len > 7 && wcsicmp(streamname + len - 6,
4723 							      L":$DATA") == 0)
4724 		    {
4725 			streamname[len - 6] = 0;
4726 			copy_substream(sh, &context, tow, streamname,
4727 						    (long)sid.Size.u.LowPart);
4728 		    }
4729 		}
4730 
4731 		/* Advance to the next stream.  We might try seeking too far,
4732 		 * but BackupSeek() doesn't skip over stream borders, thus
4733 		 * that's OK. */
4734 		(void)BackupSeek(sh, sid.Size.LowPart, sid.Size.u.HighPart,
4735 							  &lo, &hi, &context);
4736 	    }
4737 
4738 	    /* Clear the context. */
4739 	    (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context);
4740 
4741 	    CloseHandle(sh);
4742 	}
4743     }
4744     vim_free(fromw);
4745     vim_free(tow);
4746 }
4747 #endif
4748 
4749 /*
4750  * Copy file attributes from file "from" to file "to".
4751  * For Windows NT and later we copy info streams.
4752  * Always returns zero, errors are ignored.
4753  */
4754     int
4755 mch_copy_file_attribute(char_u *from, char_u *to)
4756 {
4757 #ifdef FEAT_MBYTE
4758     /* File streams only work on Windows NT and later. */
4759     PlatformId();
4760     if (g_PlatformId == VER_PLATFORM_WIN32_NT)
4761 	copy_infostreams(from, to);
4762 #endif
4763     return 0;
4764 }
4765 
4766 #if defined(MYRESETSTKOFLW) || defined(PROTO)
4767 /*
4768  * Recreate a destroyed stack guard page in win32.
4769  * Written by Benjamin Peterson.
4770  */
4771 
4772 /* These magic numbers are from the MS header files */
4773 #define MIN_STACK_WIN9X 17
4774 #define MIN_STACK_WINNT 2
4775 
4776 /*
4777  * This function does the same thing as _resetstkoflw(), which is only
4778  * available in DevStudio .net and later.
4779  * Returns 0 for failure, 1 for success.
4780  */
4781     int
4782 myresetstkoflw(void)
4783 {
4784     BYTE	*pStackPtr;
4785     BYTE	*pGuardPage;
4786     BYTE	*pStackBase;
4787     BYTE	*pLowestPossiblePage;
4788     MEMORY_BASIC_INFORMATION mbi;
4789     SYSTEM_INFO si;
4790     DWORD	nPageSize;
4791     DWORD	dummy;
4792 
4793     /* This code will not work on win32s. */
4794     PlatformId();
4795     if (g_PlatformId == VER_PLATFORM_WIN32s)
4796 	return 0;
4797 
4798     /* We need to know the system page size. */
4799     GetSystemInfo(&si);
4800     nPageSize = si.dwPageSize;
4801 
4802     /* ...and the current stack pointer */
4803     pStackPtr = (BYTE*)_alloca(1);
4804 
4805     /* ...and the base of the stack. */
4806     if (VirtualQuery(pStackPtr, &mbi, sizeof mbi) == 0)
4807 	return 0;
4808     pStackBase = (BYTE*)mbi.AllocationBase;
4809 
4810     /* ...and the page thats min_stack_req pages away from stack base; this is
4811      * the lowest page we could use. */
4812     pLowestPossiblePage = pStackBase + ((g_PlatformId == VER_PLATFORM_WIN32_NT)
4813 			     ? MIN_STACK_WINNT : MIN_STACK_WIN9X) * nPageSize;
4814 
4815     /* On Win95, we want the next page down from the end of the stack. */
4816     if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS)
4817     {
4818 	/* Find the page that's only 1 page down from the page that the stack
4819 	 * ptr is in. */
4820 	pGuardPage = (BYTE*)((DWORD)nPageSize * (((DWORD)pStackPtr
4821 						    / (DWORD)nPageSize) - 1));
4822 	if (pGuardPage < pLowestPossiblePage)
4823 	    return 0;
4824 
4825 	/* Apply the noaccess attribute to the page -- there's no guard
4826 	 * attribute in win95-type OSes. */
4827 	if (!VirtualProtect(pGuardPage, nPageSize, PAGE_NOACCESS, &dummy))
4828 	    return 0;
4829     }
4830     else
4831     {
4832 	/* On NT, however, we want the first committed page in the stack Start
4833 	 * at the stack base and move forward through memory until we find a
4834 	 * committed block. */
4835 	BYTE *pBlock = pStackBase;
4836 
4837 	while (1)
4838 	{
4839 	    if (VirtualQuery(pBlock, &mbi, sizeof mbi) == 0)
4840 		return 0;
4841 
4842 	    pBlock += mbi.RegionSize;
4843 
4844 	    if (mbi.State & MEM_COMMIT)
4845 		break;
4846 	}
4847 
4848 	/* mbi now describes the first committed block in the stack. */
4849 	if (mbi.Protect & PAGE_GUARD)
4850 	    return 1;
4851 
4852 	/* decide where the guard page should start */
4853 	if ((long_u)(mbi.BaseAddress) < (long_u)pLowestPossiblePage)
4854 	    pGuardPage = pLowestPossiblePage;
4855 	else
4856 	    pGuardPage = (BYTE*)mbi.BaseAddress;
4857 
4858 	/* allocate the guard page */
4859 	if (!VirtualAlloc(pGuardPage, nPageSize, MEM_COMMIT, PAGE_READWRITE))
4860 	    return 0;
4861 
4862 	/* apply the guard attribute to the page */
4863 	if (!VirtualProtect(pGuardPage, nPageSize, PAGE_READWRITE | PAGE_GUARD,
4864 								      &dummy))
4865 	    return 0;
4866     }
4867 
4868     return 1;
4869 }
4870 
4871 #endif
4872