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