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