xref: /vim-8.2.3635/src/os_win32.c (revision 2bf24176)
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 "vim.h"
24 
25 #ifdef FEAT_MZSCHEME
26 # include "if_mzsch.h"
27 #endif
28 
29 #include <sys/types.h>
30 #include <signal.h>
31 #include <limits.h>
32 
33 /* cproto fails on missing include files */
34 #ifndef PROTO
35 # include <process.h>
36 #endif
37 
38 #undef chdir
39 #ifdef __GNUC__
40 # ifndef __MINGW32__
41 #  include <dirent.h>
42 # endif
43 #else
44 # include <direct.h>
45 #endif
46 
47 #ifndef PROTO
48 # if defined(FEAT_TITLE) && !defined(FEAT_GUI_W32)
49 #  include <shellapi.h>
50 # endif
51 #endif
52 
53 #ifdef __MINGW32__
54 # ifndef FROM_LEFT_1ST_BUTTON_PRESSED
55 #  define FROM_LEFT_1ST_BUTTON_PRESSED    0x0001
56 # endif
57 # ifndef RIGHTMOST_BUTTON_PRESSED
58 #  define RIGHTMOST_BUTTON_PRESSED	  0x0002
59 # endif
60 # ifndef FROM_LEFT_2ND_BUTTON_PRESSED
61 #  define FROM_LEFT_2ND_BUTTON_PRESSED    0x0004
62 # endif
63 # ifndef FROM_LEFT_3RD_BUTTON_PRESSED
64 #  define FROM_LEFT_3RD_BUTTON_PRESSED    0x0008
65 # endif
66 # ifndef FROM_LEFT_4TH_BUTTON_PRESSED
67 #  define FROM_LEFT_4TH_BUTTON_PRESSED    0x0010
68 # endif
69 
70 /*
71  * EventFlags
72  */
73 # ifndef MOUSE_MOVED
74 #  define MOUSE_MOVED   0x0001
75 # endif
76 # ifndef DOUBLE_CLICK
77 #  define DOUBLE_CLICK  0x0002
78 # endif
79 #endif
80 
81 /* Record all output and all keyboard & mouse input */
82 /* #define MCH_WRITE_DUMP */
83 
84 #ifdef MCH_WRITE_DUMP
85 FILE* fdDump = NULL;
86 #endif
87 
88 /*
89  * When generating prototypes for Win32 on Unix, these lines make the syntax
90  * errors disappear.  They do not need to be correct.
91  */
92 #ifdef PROTO
93 #define WINAPI
94 #define WINBASEAPI
95 typedef char * LPCSTR;
96 typedef char * LPWSTR;
97 typedef int ACCESS_MASK;
98 typedef int BOOL;
99 typedef int COLORREF;
100 typedef int CONSOLE_CURSOR_INFO;
101 typedef int COORD;
102 typedef int DWORD;
103 typedef int HANDLE;
104 typedef int HDC;
105 typedef int HFONT;
106 typedef int HICON;
107 typedef int HINSTANCE;
108 typedef int HWND;
109 typedef int INPUT_RECORD;
110 typedef int KEY_EVENT_RECORD;
111 typedef int LOGFONT;
112 typedef int LPBOOL;
113 typedef int LPCTSTR;
114 typedef int LPDWORD;
115 typedef int LPSTR;
116 typedef int LPTSTR;
117 typedef int LPVOID;
118 typedef int MOUSE_EVENT_RECORD;
119 typedef int PACL;
120 typedef int PDWORD;
121 typedef int PHANDLE;
122 typedef int PRINTDLG;
123 typedef int PSECURITY_DESCRIPTOR;
124 typedef int PSID;
125 typedef int SECURITY_INFORMATION;
126 typedef int SHORT;
127 typedef int SMALL_RECT;
128 typedef int TEXTMETRIC;
129 typedef int TOKEN_INFORMATION_CLASS;
130 typedef int TRUSTEE;
131 typedef int WORD;
132 typedef int WCHAR;
133 typedef void VOID;
134 typedef int BY_HANDLE_FILE_INFORMATION;
135 typedef int SE_OBJECT_TYPE;
136 typedef int PSNSECINFO;
137 typedef int PSNSECINFOW;
138 typedef int STARTUPINFO;
139 typedef int PROCESS_INFORMATION;
140 #endif
141 
142 #ifndef FEAT_GUI_W32
143 /* Undocumented API in kernel32.dll needed to work around dead key bug in
144  * console-mode applications in NT 4.0.  If you switch keyboard layouts
145  * in a console app to a layout that includes dead keys and then hit a
146  * dead key, a call to ToAscii will trash the stack.  My thanks to Ian James
147  * and Michael Dietrich for helping me figure out this workaround.
148  */
149 
150 /* WINBASEAPI BOOL WINAPI GetConsoleKeyboardLayoutNameA(LPSTR); */
151 #ifndef WINBASEAPI
152 # define WINBASEAPI __stdcall
153 #endif
154 #if defined(__BORLANDC__)
155 typedef BOOL (__stdcall *PFNGCKLN)(LPSTR);
156 #else
157 typedef WINBASEAPI BOOL (WINAPI *PFNGCKLN)(LPSTR);
158 #endif
159 static PFNGCKLN    s_pfnGetConsoleKeyboardLayoutName = NULL;
160 #endif
161 
162 #if defined(__BORLANDC__)
163 /* Strangely Borland uses a non-standard name. */
164 # define wcsicmp(a, b) wcscmpi((a), (b))
165 #endif
166 
167 #ifndef PROTO
168 
169 /* Enable common dialogs input unicode from IME if possible. */
170 #ifdef FEAT_MBYTE
171 LRESULT (WINAPI *pDispatchMessage)(CONST MSG *) = DispatchMessage;
172 BOOL (WINAPI *pGetMessage)(LPMSG, HWND, UINT, UINT) = GetMessage;
173 BOOL (WINAPI *pIsDialogMessage)(HWND, LPMSG) = IsDialogMessage;
174 BOOL (WINAPI *pPeekMessage)(LPMSG, HWND, UINT, UINT, UINT) = PeekMessage;
175 #endif
176 
177 #endif /* PROTO */
178 
179 #ifndef FEAT_GUI_W32
180 /* Win32 Console handles for input and output */
181 static HANDLE g_hConIn  = INVALID_HANDLE_VALUE;
182 static HANDLE g_hConOut = INVALID_HANDLE_VALUE;
183 
184 /* Win32 Screen buffer,coordinate,console I/O information */
185 static SMALL_RECT g_srScrollRegion;
186 static COORD	  g_coord;  /* 0-based, but external coords are 1-based */
187 
188 /* The attribute of the screen when the editor was started */
189 static WORD  g_attrDefault = 7;  /* lightgray text on black background */
190 static WORD  g_attrCurrent;
191 
192 static int g_fCBrkPressed = FALSE;  /* set by ctrl-break interrupt */
193 static int g_fCtrlCPressed = FALSE; /* set when ctrl-C or ctrl-break detected */
194 static int g_fForceExit = FALSE;    /* set when forcefully exiting */
195 
196 static void termcap_mode_start(void);
197 static void termcap_mode_end(void);
198 static void clear_chars(COORD coord, DWORD n);
199 static void clear_screen(void);
200 static void clear_to_end_of_display(void);
201 static void clear_to_end_of_line(void);
202 static void scroll(unsigned cLines);
203 static void set_scroll_region(unsigned left, unsigned top,
204 			      unsigned right, unsigned bottom);
205 static void insert_lines(unsigned cLines);
206 static void delete_lines(unsigned cLines);
207 static void gotoxy(unsigned x, unsigned y);
208 static void normvideo(void);
209 static void textattr(WORD wAttr);
210 static void textcolor(WORD wAttr);
211 static void textbackground(WORD wAttr);
212 static void standout(void);
213 static void standend(void);
214 static void visual_bell(void);
215 static void cursor_visible(BOOL fVisible);
216 static DWORD write_chars(char_u *pchBuf, DWORD cbToWrite);
217 static WCHAR tgetch(int *pmodifiers, WCHAR *pch2);
218 static void create_conin(void);
219 static int s_cursor_visible = TRUE;
220 static int did_create_conin = FALSE;
221 #else
222 static int s_dont_use_vimrun = TRUE;
223 static int need_vimrun_warning = FALSE;
224 static char *vimrun_path = "vimrun ";
225 #endif
226 
227 static int win32_getattrs(char_u *name);
228 static int win32_setattrs(char_u *name, int attrs);
229 static int win32_set_archive(char_u *name);
230 
231 #ifndef FEAT_GUI_W32
232 static int suppress_winsize = 1;	/* don't fiddle with console */
233 #endif
234 
235 static char_u *exe_path = NULL;
236 
237 static BOOL win8_or_later = FALSE;
238 
239 /*
240  * Version of ReadConsoleInput() that works with IME.
241  * Works around problems on Windows 8.
242  */
243     static BOOL
244 read_console_input(
245     HANDLE	    hInput,
246     INPUT_RECORD    *lpBuffer,
247     DWORD	    nLength,
248     LPDWORD	    lpEvents)
249 {
250     enum
251     {
252 	IRSIZE = 10
253     };
254     static INPUT_RECORD s_irCache[IRSIZE];
255     static DWORD s_dwIndex = 0;
256     static DWORD s_dwMax = 0;
257     DWORD dwEvents;
258     int head;
259     int tail;
260     int i;
261 
262     if (nLength == -2)
263 	return (s_dwMax > 0) ? TRUE : FALSE;
264 
265     if (!win8_or_later)
266     {
267 	if (nLength == -1)
268 	    return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
269 	return ReadConsoleInputW(hInput, lpBuffer, 1, &dwEvents);
270     }
271 
272     if (s_dwMax == 0)
273     {
274 	if (nLength == -1)
275 	    return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
276 	if (!ReadConsoleInputW(hInput, s_irCache, IRSIZE, &dwEvents))
277 	    return FALSE;
278 	s_dwIndex = 0;
279 	s_dwMax = dwEvents;
280 	if (dwEvents == 0)
281 	{
282 	    *lpEvents = 0;
283 	    return TRUE;
284 	}
285 
286 	if (s_dwMax > 1)
287 	{
288 	    head = 0;
289 	    tail = s_dwMax - 1;
290 	    while (head != tail)
291 	    {
292 		if (s_irCache[head].EventType == WINDOW_BUFFER_SIZE_EVENT
293 			&& s_irCache[head + 1].EventType
294 						  == WINDOW_BUFFER_SIZE_EVENT)
295 		{
296 		    /* Remove duplicate event to avoid flicker. */
297 		    for (i = head; i < tail; ++i)
298 			s_irCache[i] = s_irCache[i + 1];
299 		    --tail;
300 		    continue;
301 		}
302 		head++;
303 	    }
304 	    s_dwMax = tail + 1;
305 	}
306     }
307 
308     *lpBuffer = s_irCache[s_dwIndex];
309     if (!(nLength == -1 || nLength == -2) && ++s_dwIndex >= s_dwMax)
310 	s_dwMax = 0;
311     *lpEvents = 1;
312     return TRUE;
313 }
314 
315 /*
316  * Version of PeekConsoleInput() that works with IME.
317  */
318     static BOOL
319 peek_console_input(
320     HANDLE	    hInput,
321     INPUT_RECORD    *lpBuffer,
322     DWORD	    nLength,
323     LPDWORD	    lpEvents)
324 {
325     return read_console_input(hInput, lpBuffer, -1, lpEvents);
326 }
327 
328     static DWORD
329 msg_wait_for_multiple_objects(
330     DWORD    nCount,
331     LPHANDLE pHandles,
332     BOOL     fWaitAll,
333     DWORD    dwMilliseconds,
334     DWORD    dwWakeMask)
335 {
336     if (read_console_input(NULL, NULL, -2, NULL))
337 	return WAIT_OBJECT_0;
338     return MsgWaitForMultipleObjects(nCount, pHandles, fWaitAll,
339 				     dwMilliseconds, dwWakeMask);
340 }
341 
342     static DWORD
343 wait_for_single_object(
344     HANDLE hHandle,
345     DWORD dwMilliseconds)
346 {
347     if (read_console_input(NULL, NULL, -2, NULL))
348 	return WAIT_OBJECT_0;
349     return WaitForSingleObject(hHandle, dwMilliseconds);
350 }
351 
352     static void
353 get_exe_name(void)
354 {
355     /* Maximum length of $PATH is more than MAXPATHL.  8191 is often mentioned
356      * as the maximum length that works (plus a NUL byte). */
357 #define MAX_ENV_PATH_LEN 8192
358     char	temp[MAX_ENV_PATH_LEN];
359     char_u	*p;
360 
361     if (exe_name == NULL)
362     {
363 	/* store the name of the executable, may be used for $VIM */
364 	GetModuleFileName(NULL, temp, MAX_ENV_PATH_LEN - 1);
365 	if (*temp != NUL)
366 	    exe_name = FullName_save((char_u *)temp, FALSE);
367     }
368 
369     if (exe_path == NULL && exe_name != NULL)
370     {
371 	exe_path = vim_strnsave(exe_name,
372 				     (int)(gettail_sep(exe_name) - exe_name));
373 	if (exe_path != NULL)
374 	{
375 	    /* Append our starting directory to $PATH, so that when doing
376 	     * "!xxd" it's found in our starting directory.  Needed because
377 	     * SearchPath() also looks there. */
378 	    p = mch_getenv("PATH");
379 	    if (p == NULL
380 		       || STRLEN(p) + STRLEN(exe_path) + 2 < MAX_ENV_PATH_LEN)
381 	    {
382 		if (p == NULL || *p == NUL)
383 		    temp[0] = NUL;
384 		else
385 		{
386 		    STRCPY(temp, p);
387 		    STRCAT(temp, ";");
388 		}
389 		STRCAT(temp, exe_path);
390 		vim_setenv((char_u *)"PATH", temp);
391 	    }
392 	}
393     }
394 }
395 
396 /*
397  * Unescape characters in "p" that appear in "escaped".
398  */
399     static void
400 unescape_shellxquote(char_u *p, char_u *escaped)
401 {
402     int	    l = (int)STRLEN(p);
403     int	    n;
404 
405     while (*p != NUL)
406     {
407 	if (*p == '^' && vim_strchr(escaped, p[1]) != NULL)
408 	    mch_memmove(p, p + 1, l--);
409 #ifdef FEAT_MBYTE
410 	n = (*mb_ptr2len)(p);
411 #else
412 	n = 1;
413 #endif
414 	p += n;
415 	l -= n;
416     }
417 }
418 
419 /*
420  * Load library "name".
421  */
422     HINSTANCE
423 vimLoadLib(char *name)
424 {
425     HINSTANCE	dll = NULL;
426     char	old_dir[MAXPATHL];
427 
428     /* NOTE: Do not use mch_dirname() and mch_chdir() here, they may call
429      * vimLoadLib() recursively, which causes a stack overflow. */
430     if (exe_path == NULL)
431 	get_exe_name();
432     if (exe_path != NULL)
433     {
434 #ifdef FEAT_MBYTE
435 	WCHAR old_dirw[MAXPATHL];
436 
437 	if (GetCurrentDirectoryW(MAXPATHL, old_dirw) != 0)
438 	{
439 	    /* Change directory to where the executable is, both to make
440 	     * sure we find a .dll there and to avoid looking for a .dll
441 	     * in the current directory. */
442 	    SetCurrentDirectory(exe_path);
443 	    dll = LoadLibrary(name);
444 	    SetCurrentDirectoryW(old_dirw);
445 	    return dll;
446 	}
447 	/* Retry with non-wide function (for Windows 98). */
448 	if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
449 #endif
450 	    if (GetCurrentDirectory(MAXPATHL, old_dir) != 0)
451 	    {
452 		/* Change directory to where the executable is, both to make
453 		 * sure we find a .dll there and to avoid looking for a .dll
454 		 * in the current directory. */
455 		SetCurrentDirectory(exe_path);
456 		dll = LoadLibrary(name);
457 		SetCurrentDirectory(old_dir);
458 	    }
459     }
460     return dll;
461 }
462 
463 #if defined(DYNAMIC_GETTEXT) || defined(PROTO)
464 # ifndef GETTEXT_DLL
465 #  define GETTEXT_DLL "libintl.dll"
466 # endif
467 /* Dummy functions */
468 static char *null_libintl_gettext(const char *);
469 static char *null_libintl_textdomain(const char *);
470 static char *null_libintl_bindtextdomain(const char *, const char *);
471 static char *null_libintl_bind_textdomain_codeset(const char *, const char *);
472 
473 static HINSTANCE hLibintlDLL = NULL;
474 char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
475 char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
476 char *(*dyn_libintl_bindtextdomain)(const char *, const char *)
477 						= null_libintl_bindtextdomain;
478 char *(*dyn_libintl_bind_textdomain_codeset)(const char *, const char *)
479 				       = null_libintl_bind_textdomain_codeset;
480 
481     int
482 dyn_libintl_init(char *libname)
483 {
484     int i;
485     static struct
486     {
487 	char	    *name;
488 	FARPROC	    *ptr;
489     } libintl_entry[] =
490     {
491 	{"gettext", (FARPROC*)&dyn_libintl_gettext},
492 	{"textdomain", (FARPROC*)&dyn_libintl_textdomain},
493 	{"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
494 	{NULL, NULL}
495     };
496 
497     /* No need to initialize twice. */
498     if (hLibintlDLL)
499 	return 1;
500     /* Load gettext library (libintl.dll) */
501     hLibintlDLL = vimLoadLib(libname != NULL ? libname : GETTEXT_DLL);
502     if (!hLibintlDLL)
503     {
504 	if (p_verbose > 0)
505 	{
506 	    verbose_enter();
507 	    EMSG2(_(e_loadlib), GETTEXT_DLL);
508 	    verbose_leave();
509 	}
510 	return 0;
511     }
512     for (i = 0; libintl_entry[i].name != NULL
513 					 && libintl_entry[i].ptr != NULL; ++i)
514     {
515 	if ((*libintl_entry[i].ptr = (FARPROC)GetProcAddress(hLibintlDLL,
516 					      libintl_entry[i].name)) == NULL)
517 	{
518 	    dyn_libintl_end();
519 	    if (p_verbose > 0)
520 	    {
521 		verbose_enter();
522 		EMSG2(_(e_loadfunc), libintl_entry[i].name);
523 		verbose_leave();
524 	    }
525 	    return 0;
526 	}
527     }
528 
529     /* The bind_textdomain_codeset() function is optional. */
530     dyn_libintl_bind_textdomain_codeset = (void *)GetProcAddress(hLibintlDLL,
531 						   "bind_textdomain_codeset");
532     if (dyn_libintl_bind_textdomain_codeset == NULL)
533 	dyn_libintl_bind_textdomain_codeset =
534 					 null_libintl_bind_textdomain_codeset;
535 
536     return 1;
537 }
538 
539     void
540 dyn_libintl_end()
541 {
542     if (hLibintlDLL)
543 	FreeLibrary(hLibintlDLL);
544     hLibintlDLL			= NULL;
545     dyn_libintl_gettext		= null_libintl_gettext;
546     dyn_libintl_textdomain	= null_libintl_textdomain;
547     dyn_libintl_bindtextdomain	= null_libintl_bindtextdomain;
548     dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset;
549 }
550 
551 /*ARGSUSED*/
552     static char *
553 null_libintl_gettext(const char *msgid)
554 {
555     return (char*)msgid;
556 }
557 
558 /*ARGSUSED*/
559     static char *
560 null_libintl_bindtextdomain(const char *domainname, const char *dirname)
561 {
562     return NULL;
563 }
564 
565 /*ARGSUSED*/
566     static char *
567 null_libintl_bind_textdomain_codeset(const char *domainname,
568 							  const char *codeset)
569 {
570     return NULL;
571 }
572 
573 /*ARGSUSED*/
574     static char *
575 null_libintl_textdomain(const char *domainname)
576 {
577     return NULL;
578 }
579 
580 #endif /* DYNAMIC_GETTEXT */
581 
582 /* This symbol is not defined in older versions of the SDK or Visual C++ */
583 
584 #ifndef VER_PLATFORM_WIN32_WINDOWS
585 # define VER_PLATFORM_WIN32_WINDOWS 1
586 #endif
587 
588 DWORD g_PlatformId;
589 
590 #ifdef HAVE_ACL
591 # ifndef PROTO
592 #  include <aclapi.h>
593 # endif
594 # ifndef PROTECTED_DACL_SECURITY_INFORMATION
595 #  define PROTECTED_DACL_SECURITY_INFORMATION	0x80000000L
596 # endif
597 
598 /*
599  * These are needed to dynamically load the ADVAPI DLL, which is not
600  * implemented under Windows 95 (and causes VIM to crash)
601  */
602 typedef DWORD (WINAPI *PSNSECINFO) (LPSTR, SE_OBJECT_TYPE,
603 	SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
604 typedef DWORD (WINAPI *PGNSECINFO) (LPSTR, SE_OBJECT_TYPE,
605 	SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *,
606 	PSECURITY_DESCRIPTOR *);
607 # ifdef FEAT_MBYTE
608 typedef DWORD (WINAPI *PSNSECINFOW) (LPWSTR, SE_OBJECT_TYPE,
609 	SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
610 typedef DWORD (WINAPI *PGNSECINFOW) (LPWSTR, SE_OBJECT_TYPE,
611 	SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *,
612 	PSECURITY_DESCRIPTOR *);
613 # endif
614 
615 static HANDLE advapi_lib = NULL;	/* Handle for ADVAPI library */
616 static PSNSECINFO pSetNamedSecurityInfo;
617 static PGNSECINFO pGetNamedSecurityInfo;
618 # ifdef FEAT_MBYTE
619 static PSNSECINFOW pSetNamedSecurityInfoW;
620 static PGNSECINFOW pGetNamedSecurityInfoW;
621 # endif
622 #endif
623 
624 typedef BOOL (WINAPI *PSETHANDLEINFORMATION)(HANDLE, DWORD, DWORD);
625 
626 static BOOL allowPiping = FALSE;
627 static PSETHANDLEINFORMATION pSetHandleInformation;
628 
629 #ifdef HAVE_ACL
630 /*
631  * Enables or disables the specified privilege.
632  */
633     static BOOL
634 win32_enable_privilege(LPTSTR lpszPrivilege, BOOL bEnable)
635 {
636     BOOL		bResult;
637     LUID		luid;
638     HANDLE		hToken;
639     TOKEN_PRIVILEGES	tokenPrivileges;
640 
641     if (!OpenProcessToken(GetCurrentProcess(),
642 		TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
643 	return FALSE;
644 
645     if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
646     {
647 	CloseHandle(hToken);
648 	return FALSE;
649     }
650 
651     tokenPrivileges.PrivilegeCount	     = 1;
652     tokenPrivileges.Privileges[0].Luid       = luid;
653     tokenPrivileges.Privileges[0].Attributes = bEnable ?
654 						    SE_PRIVILEGE_ENABLED : 0;
655 
656     bResult = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges,
657 	    sizeof(TOKEN_PRIVILEGES), NULL, NULL);
658 
659     CloseHandle(hToken);
660 
661     return bResult && GetLastError() == ERROR_SUCCESS;
662 }
663 #endif
664 
665 /*
666  * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
667  * VER_PLATFORM_WIN32_WINDOWS (Win95).
668  */
669     void
670 PlatformId(void)
671 {
672     static int done = FALSE;
673 
674     if (!done)
675     {
676 	OSVERSIONINFO ovi;
677 
678 	ovi.dwOSVersionInfoSize = sizeof(ovi);
679 	GetVersionEx(&ovi);
680 
681 	g_PlatformId = ovi.dwPlatformId;
682 
683 	if ((ovi.dwMajorVersion == 6 && ovi.dwMinorVersion >= 2)
684 		|| ovi.dwMajorVersion > 6)
685 	    win8_or_later = TRUE;
686 
687 #ifdef HAVE_ACL
688 	/*
689 	 * Load the ADVAPI runtime if we are on anything
690 	 * other than Windows 95
691 	 */
692 	if (g_PlatformId == VER_PLATFORM_WIN32_NT)
693 	{
694 	    /*
695 	     * do this load.  Problems: Doesn't unload at end of run (this is
696 	     * theoretically okay, since Windows should unload it when VIM
697 	     * terminates).  Should we be using the 'mch_libcall' routines?
698 	     * Seems like a lot of overhead to load/unload ADVAPI32.DLL each
699 	     * time we verify security...
700 	     */
701 	    advapi_lib = vimLoadLib("ADVAPI32.DLL");
702 	    if (advapi_lib != NULL)
703 	    {
704 		pSetNamedSecurityInfo = (PSNSECINFO)GetProcAddress(advapi_lib,
705 						      "SetNamedSecurityInfoA");
706 		pGetNamedSecurityInfo = (PGNSECINFO)GetProcAddress(advapi_lib,
707 						      "GetNamedSecurityInfoA");
708 # ifdef FEAT_MBYTE
709 		pSetNamedSecurityInfoW = (PSNSECINFOW)GetProcAddress(advapi_lib,
710 						      "SetNamedSecurityInfoW");
711 		pGetNamedSecurityInfoW = (PGNSECINFOW)GetProcAddress(advapi_lib,
712 						      "GetNamedSecurityInfoW");
713 # endif
714 		if (pSetNamedSecurityInfo == NULL
715 			|| pGetNamedSecurityInfo == NULL
716 # ifdef FEAT_MBYTE
717 			|| pSetNamedSecurityInfoW == NULL
718 			|| pGetNamedSecurityInfoW == NULL
719 # endif
720 			)
721 		{
722 		    /* If we can't get the function addresses, set advapi_lib
723 		     * to NULL so that we don't use them. */
724 		    FreeLibrary(advapi_lib);
725 		    advapi_lib = NULL;
726 		}
727 		/* Enable privilege for getting or setting SACLs. */
728 		win32_enable_privilege(SE_SECURITY_NAME, TRUE);
729 	    }
730 	}
731 #endif
732 	/*
733 	 * If we are on windows NT, try to load the pipe functions, only
734 	 * available from Win2K.
735 	 */
736 	if (g_PlatformId == VER_PLATFORM_WIN32_NT)
737 	{
738 	    HANDLE kernel32 = GetModuleHandle("kernel32");
739 	    pSetHandleInformation = (PSETHANDLEINFORMATION)GetProcAddress(
740 					    kernel32, "SetHandleInformation");
741 
742 	    allowPiping = pSetHandleInformation != NULL;
743 	}
744 	done = TRUE;
745     }
746 }
747 
748 /*
749  * Return TRUE when running on Windows 95 (or 98 or ME).
750  * Only to be used after mch_init().
751  */
752     int
753 mch_windows95(void)
754 {
755     return g_PlatformId == VER_PLATFORM_WIN32_WINDOWS;
756 }
757 
758 #ifdef FEAT_GUI_W32
759 /*
760  * Used to work around the "can't do synchronous spawn"
761  * problem on Win32s, without resorting to Universal Thunk.
762  */
763 static int old_num_windows;
764 static int num_windows;
765 
766 /*ARGSUSED*/
767     static BOOL CALLBACK
768 win32ssynch_cb(HWND hwnd, LPARAM lparam)
769 {
770     num_windows++;
771     return TRUE;
772 }
773 #endif
774 
775 #ifndef FEAT_GUI_W32
776 
777 #define SHIFT  (SHIFT_PRESSED)
778 #define CTRL   (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
779 #define ALT    (RIGHT_ALT_PRESSED  | LEFT_ALT_PRESSED)
780 #define ALT_GR (RIGHT_ALT_PRESSED  | LEFT_CTRL_PRESSED)
781 
782 
783 /* When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
784  * We map function keys to their ANSI terminal equivalents, as produced
785  * by ANSI.SYS, for compatibility with the MS-DOS version of Vim.  Any
786  * ANSI key with a value >= '\300' is nonstandard, but provided anyway
787  * so that the user can have access to all SHIFT-, CTRL-, and ALT-
788  * combinations of function/arrow/etc keys.
789  */
790 
791 static const struct
792 {
793     WORD    wVirtKey;
794     BOOL    fAnsiKey;
795     int	    chAlone;
796     int	    chShift;
797     int	    chCtrl;
798     int	    chAlt;
799 } VirtKeyMap[] =
800 {
801 
802 /*    Key	ANSI	alone	shift	ctrl	    alt */
803     { VK_ESCAPE,FALSE,	ESC,	ESC,	ESC,	    ESC,    },
804 
805     { VK_F1,	TRUE,	';',	'T',	'^',	    'h', },
806     { VK_F2,	TRUE,	'<',	'U',	'_',	    'i', },
807     { VK_F3,	TRUE,	'=',	'V',	'`',	    'j', },
808     { VK_F4,	TRUE,	'>',	'W',	'a',	    'k', },
809     { VK_F5,	TRUE,	'?',	'X',	'b',	    'l', },
810     { VK_F6,	TRUE,	'@',	'Y',	'c',	    'm', },
811     { VK_F7,	TRUE,	'A',	'Z',	'd',	    'n', },
812     { VK_F8,	TRUE,	'B',	'[',	'e',	    'o', },
813     { VK_F9,	TRUE,	'C',	'\\',	'f',	    'p', },
814     { VK_F10,	TRUE,	'D',	']',	'g',	    'q', },
815     { VK_F11,	TRUE,	'\205',	'\207',	'\211',	    '\213', },
816     { VK_F12,	TRUE,	'\206',	'\210',	'\212',	    '\214', },
817 
818     { VK_HOME,	TRUE,	'G',	'\302',	'w',	    '\303', },
819     { VK_UP,	TRUE,	'H',	'\304',	'\305',	    '\306', },
820     { VK_PRIOR,	TRUE,	'I',	'\307',	'\204',	    '\310', }, /*PgUp*/
821     { VK_LEFT,	TRUE,	'K',	'\311',	's',	    '\312', },
822     { VK_RIGHT,	TRUE,	'M',	'\313',	't',	    '\314', },
823     { VK_END,	TRUE,	'O',	'\315',	'u',	    '\316', },
824     { VK_DOWN,	TRUE,	'P',	'\317',	'\320',	    '\321', },
825     { VK_NEXT,	TRUE,	'Q',	'\322',	'v',	    '\323', }, /*PgDn*/
826     { VK_INSERT,TRUE,	'R',	'\324',	'\325',	    '\326', },
827     { VK_DELETE,TRUE,	'S',	'\327',	'\330',	    '\331', },
828 
829     { VK_SNAPSHOT,TRUE,	0,	0,	0,	    'r', }, /*PrtScrn*/
830 
831 #if 0
832     /* Most people don't have F13-F20, but what the hell... */
833     { VK_F13,	TRUE,	'\332',	'\333',	'\334',	    '\335', },
834     { VK_F14,	TRUE,	'\336',	'\337',	'\340',	    '\341', },
835     { VK_F15,	TRUE,	'\342',	'\343',	'\344',	    '\345', },
836     { VK_F16,	TRUE,	'\346',	'\347',	'\350',	    '\351', },
837     { VK_F17,	TRUE,	'\352',	'\353',	'\354',	    '\355', },
838     { VK_F18,	TRUE,	'\356',	'\357',	'\360',	    '\361', },
839     { VK_F19,	TRUE,	'\362',	'\363',	'\364',	    '\365', },
840     { VK_F20,	TRUE,	'\366',	'\367',	'\370',	    '\371', },
841 #endif
842     { VK_ADD,	TRUE,   'N',    'N',    'N',	'N',	}, /* keyp '+' */
843     { VK_SUBTRACT, TRUE,'J',	'J',    'J',	'J',	}, /* keyp '-' */
844  /* { VK_DIVIDE,   TRUE,'N',	'N',    'N',	'N',	},    keyp '/' */
845     { VK_MULTIPLY, TRUE,'7',	'7',    '7',	'7',	}, /* keyp '*' */
846 
847     { VK_NUMPAD0,TRUE,  '\332',	'\333',	'\334',	    '\335', },
848     { VK_NUMPAD1,TRUE,  '\336',	'\337',	'\340',	    '\341', },
849     { VK_NUMPAD2,TRUE,  '\342',	'\343',	'\344',	    '\345', },
850     { VK_NUMPAD3,TRUE,  '\346',	'\347',	'\350',	    '\351', },
851     { VK_NUMPAD4,TRUE,  '\352',	'\353',	'\354',	    '\355', },
852     { VK_NUMPAD5,TRUE,  '\356',	'\357',	'\360',	    '\361', },
853     { VK_NUMPAD6,TRUE,  '\362',	'\363',	'\364',	    '\365', },
854     { VK_NUMPAD7,TRUE,  '\366',	'\367',	'\370',	    '\371', },
855     { VK_NUMPAD8,TRUE,  '\372',	'\373',	'\374',	    '\375', },
856     /* Sorry, out of number space! <negri>*/
857     { VK_NUMPAD9,TRUE,  '\376',	'\377',	'\377',	    '\367', },
858 
859 };
860 
861 
862 #ifdef _MSC_VER
863 // The ToAscii bug destroys several registers.	Need to turn off optimization
864 // or the GetConsoleKeyboardLayoutName hack will fail in non-debug versions
865 # pragma warning(push)
866 # pragma warning(disable: 4748)
867 # pragma optimize("", off)
868 #endif
869 
870 #if defined(__GNUC__) && !defined(__MINGW32__)  && !defined(__CYGWIN__)
871 # define UChar UnicodeChar
872 #else
873 # define UChar uChar.UnicodeChar
874 #endif
875 
876 /* The return code indicates key code size. */
877     static int
878 #ifdef __BORLANDC__
879     __stdcall
880 #endif
881 win32_kbd_patch_key(
882     KEY_EVENT_RECORD *pker)
883 {
884     UINT uMods = pker->dwControlKeyState;
885     static int s_iIsDead = 0;
886     static WORD awAnsiCode[2];
887     static BYTE abKeystate[256];
888 
889 
890     if (s_iIsDead == 2)
891     {
892 	pker->UChar = (WCHAR) awAnsiCode[1];
893 	s_iIsDead = 0;
894 	return 1;
895     }
896 
897     if (pker->UChar != 0)
898 	return 1;
899 
900     vim_memset(abKeystate, 0, sizeof (abKeystate));
901 
902     // Should only be non-NULL on NT 4.0
903     if (s_pfnGetConsoleKeyboardLayoutName != NULL)
904     {
905 	CHAR szKLID[KL_NAMELENGTH];
906 
907 	if ((*s_pfnGetConsoleKeyboardLayoutName)(szKLID))
908 	    (void)LoadKeyboardLayout(szKLID, KLF_ACTIVATE);
909     }
910 
911     /* Clear any pending dead keys */
912     ToUnicode(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 2, 0);
913 
914     if (uMods & SHIFT_PRESSED)
915 	abKeystate[VK_SHIFT] = 0x80;
916     if (uMods & CAPSLOCK_ON)
917 	abKeystate[VK_CAPITAL] = 1;
918 
919     if ((uMods & ALT_GR) == ALT_GR)
920     {
921 	abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
922 	    abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
923     }
924 
925     s_iIsDead = ToUnicode(pker->wVirtualKeyCode, pker->wVirtualScanCode,
926 			abKeystate, awAnsiCode, 2, 0);
927 
928     if (s_iIsDead > 0)
929 	pker->UChar = (WCHAR) awAnsiCode[0];
930 
931     return s_iIsDead;
932 }
933 
934 #ifdef _MSC_VER
935 /* MUST switch optimization on again here, otherwise a call to
936  * decode_key_event() may crash (e.g. when hitting caps-lock) */
937 # pragma optimize("", on)
938 # pragma warning(pop)
939 
940 # if (_MSC_VER < 1100)
941 /* MUST turn off global optimisation for this next function, or
942  * pressing ctrl-minus in insert mode crashes Vim when built with
943  * VC4.1. -- negri. */
944 #  pragma optimize("g", off)
945 # endif
946 #endif
947 
948 static BOOL g_fJustGotFocus = FALSE;
949 
950 /*
951  * Decode a KEY_EVENT into one or two keystrokes
952  */
953     static BOOL
954 decode_key_event(
955     KEY_EVENT_RECORD	*pker,
956     WCHAR		*pch,
957     WCHAR		*pch2,
958     int			*pmodifiers,
959     BOOL		fDoPost)
960 {
961     int i;
962     const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
963 
964     *pch = *pch2 = NUL;
965     g_fJustGotFocus = FALSE;
966 
967     /* ignore key up events */
968     if (!pker->bKeyDown)
969 	return FALSE;
970 
971     /* ignore some keystrokes */
972     switch (pker->wVirtualKeyCode)
973     {
974     /* modifiers */
975     case VK_SHIFT:
976     case VK_CONTROL:
977     case VK_MENU:   /* Alt key */
978 	return FALSE;
979 
980     default:
981 	break;
982     }
983 
984     /* special cases */
985     if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->UChar == NUL)
986     {
987 	/* Ctrl-6 is Ctrl-^ */
988 	if (pker->wVirtualKeyCode == '6')
989 	{
990 	    *pch = Ctrl_HAT;
991 	    return TRUE;
992 	}
993 	/* Ctrl-2 is Ctrl-@ */
994 	else if (pker->wVirtualKeyCode == '2')
995 	{
996 	    *pch = NUL;
997 	    return TRUE;
998 	}
999 	/* Ctrl-- is Ctrl-_ */
1000 	else if (pker->wVirtualKeyCode == 0xBD)
1001 	{
1002 	    *pch = Ctrl__;
1003 	    return TRUE;
1004 	}
1005     }
1006 
1007     /* Shift-TAB */
1008     if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED))
1009     {
1010 	*pch = K_NUL;
1011 	*pch2 = '\017';
1012 	return TRUE;
1013     }
1014 
1015     for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]);  --i >= 0;  )
1016     {
1017 	if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
1018 	{
1019 	    if (nModifs == 0)
1020 		*pch = VirtKeyMap[i].chAlone;
1021 	    else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
1022 		*pch = VirtKeyMap[i].chShift;
1023 	    else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
1024 		*pch = VirtKeyMap[i].chCtrl;
1025 	    else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
1026 		*pch = VirtKeyMap[i].chAlt;
1027 
1028 	    if (*pch != 0)
1029 	    {
1030 		if (VirtKeyMap[i].fAnsiKey)
1031 		{
1032 		    *pch2 = *pch;
1033 		    *pch = K_NUL;
1034 		}
1035 
1036 		return TRUE;
1037 	    }
1038 	}
1039     }
1040 
1041     i = win32_kbd_patch_key(pker);
1042 
1043     if (i < 0)
1044 	*pch = NUL;
1045     else
1046     {
1047 	*pch = (i > 0) ? pker->UChar : NUL;
1048 
1049 	if (pmodifiers != NULL)
1050 	{
1051 	    /* Pass on the ALT key as a modifier, but only when not combined
1052 	     * with CTRL (which is ALTGR). */
1053 	    if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0)
1054 		*pmodifiers |= MOD_MASK_ALT;
1055 
1056 	    /* Pass on SHIFT only for special keys, because we don't know when
1057 	     * it's already included with the character. */
1058 	    if ((nModifs & SHIFT) != 0 && *pch <= 0x20)
1059 		*pmodifiers |= MOD_MASK_SHIFT;
1060 
1061 	    /* Pass on CTRL only for non-special keys, because we don't know
1062 	     * when it's already included with the character.  And not when
1063 	     * combined with ALT (which is ALTGR). */
1064 	    if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0
1065 					       && *pch >= 0x20 && *pch < 0x80)
1066 		*pmodifiers |= MOD_MASK_CTRL;
1067 	}
1068     }
1069 
1070     return (*pch != NUL);
1071 }
1072 
1073 #ifdef _MSC_VER
1074 # pragma optimize("", on)
1075 #endif
1076 
1077 #endif /* FEAT_GUI_W32 */
1078 
1079 
1080 #ifdef FEAT_MOUSE
1081 
1082 /*
1083  * For the GUI the mouse handling is in gui_w32.c.
1084  */
1085 # ifdef FEAT_GUI_W32
1086 /*ARGSUSED*/
1087     void
1088 mch_setmouse(int on)
1089 {
1090 }
1091 # else
1092 static int g_fMouseAvail = FALSE;   /* mouse present */
1093 static int g_fMouseActive = FALSE;  /* mouse enabled */
1094 static int g_nMouseClick = -1;	    /* mouse status */
1095 static int g_xMouse;		    /* mouse x coordinate */
1096 static int g_yMouse;		    /* mouse y coordinate */
1097 
1098 /*
1099  * Enable or disable mouse input
1100  */
1101     void
1102 mch_setmouse(int on)
1103 {
1104     DWORD cmodein;
1105 
1106     if (!g_fMouseAvail)
1107 	return;
1108 
1109     g_fMouseActive = on;
1110     GetConsoleMode(g_hConIn, &cmodein);
1111 
1112     if (g_fMouseActive)
1113 	cmodein |= ENABLE_MOUSE_INPUT;
1114     else
1115 	cmodein &= ~ENABLE_MOUSE_INPUT;
1116 
1117     SetConsoleMode(g_hConIn, cmodein);
1118 }
1119 
1120 
1121 /*
1122  * Decode a MOUSE_EVENT.  If it's a valid event, return MOUSE_LEFT,
1123  * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
1124  * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
1125  * or a MOUSE_LEFT, _MIDDLE, or _RIGHT.  We encode the button type,
1126  * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
1127  * and we return the mouse position in g_xMouse and g_yMouse.
1128  *
1129  * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
1130  * MOUSE_DRAGs and one MOUSE_RELEASE.  MOUSE_RELEASE will be followed only
1131  * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
1132  *
1133  * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
1134  * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
1135  *
1136  * Windows will send us MOUSE_MOVED notifications whenever the mouse
1137  * moves, even if it stays within the same character cell.  We ignore
1138  * all MOUSE_MOVED messages if the position hasn't really changed, and
1139  * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
1140  * we're only interested in MOUSE_DRAG).
1141  *
1142  * All of this is complicated by the code that fakes MOUSE_MIDDLE on
1143  * 2-button mouses by pressing the left & right buttons simultaneously.
1144  * In practice, it's almost impossible to click both at the same time,
1145  * so we need to delay a little.  Also, we tend not to get MOUSE_RELEASE
1146  * in such cases, if the user is clicking quickly.
1147  */
1148     static BOOL
1149 decode_mouse_event(
1150     MOUSE_EVENT_RECORD *pmer)
1151 {
1152     static int s_nOldButton = -1;
1153     static int s_nOldMouseClick = -1;
1154     static int s_xOldMouse = -1;
1155     static int s_yOldMouse = -1;
1156     static linenr_T s_old_topline = 0;
1157 #ifdef FEAT_DIFF
1158     static int s_old_topfill = 0;
1159 #endif
1160     static int s_cClicks = 1;
1161     static BOOL s_fReleased = TRUE;
1162     static DWORD s_dwLastClickTime = 0;
1163     static BOOL s_fNextIsMiddle = FALSE;
1164 
1165     static DWORD cButtons = 0;	/* number of buttons supported */
1166 
1167     const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
1168     const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
1169     const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
1170     const DWORD LEFT_RIGHT = LEFT | RIGHT;
1171 
1172     int nButton;
1173 
1174     if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons))
1175 	cButtons = 2;
1176 
1177     if (!g_fMouseAvail || !g_fMouseActive)
1178     {
1179 	g_nMouseClick = -1;
1180 	return FALSE;
1181     }
1182 
1183     /* get a spurious MOUSE_EVENT immediately after receiving focus; ignore */
1184     if (g_fJustGotFocus)
1185     {
1186 	g_fJustGotFocus = FALSE;
1187 	return FALSE;
1188     }
1189 
1190     /* unprocessed mouse click? */
1191     if (g_nMouseClick != -1)
1192 	return TRUE;
1193 
1194     nButton = -1;
1195     g_xMouse = pmer->dwMousePosition.X;
1196     g_yMouse = pmer->dwMousePosition.Y;
1197 
1198     if (pmer->dwEventFlags == MOUSE_MOVED)
1199     {
1200 	/* ignore MOUSE_MOVED events if (x, y) hasn't changed.	(We get these
1201 	 * events even when the mouse moves only within a char cell.) */
1202 	if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse)
1203 	    return FALSE;
1204     }
1205 
1206     /* If no buttons are pressed... */
1207     if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0)
1208     {
1209 	/* If the last thing returned was MOUSE_RELEASE, ignore this */
1210 	if (s_fReleased)
1211 	    return FALSE;
1212 
1213 	nButton = MOUSE_RELEASE;
1214 	s_fReleased = TRUE;
1215     }
1216     else    /* one or more buttons pressed */
1217     {
1218 	/* on a 2-button mouse, hold down left and right buttons
1219 	 * simultaneously to get MIDDLE. */
1220 
1221 	if (cButtons == 2 && s_nOldButton != MOUSE_DRAG)
1222 	{
1223 	    DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
1224 
1225 	    /* if either left or right button only is pressed, see if the
1226 	     * next mouse event has both of them pressed */
1227 	    if (dwLR == LEFT || dwLR == RIGHT)
1228 	    {
1229 		for (;;)
1230 		{
1231 		    /* wait a short time for next input event */
1232 		    if (WaitForSingleObject(g_hConIn, p_mouset / 3)
1233 							     != WAIT_OBJECT_0)
1234 			break;
1235 		    else
1236 		    {
1237 			DWORD cRecords = 0;
1238 			INPUT_RECORD ir;
1239 			MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
1240 
1241 			peek_console_input(g_hConIn, &ir, 1, &cRecords);
1242 
1243 			if (cRecords == 0 || ir.EventType != MOUSE_EVENT
1244 				|| !(pmer2->dwButtonState & LEFT_RIGHT))
1245 			    break;
1246 			else
1247 			{
1248 			    if (pmer2->dwEventFlags != MOUSE_MOVED)
1249 			    {
1250 				read_console_input(g_hConIn, &ir, 1, &cRecords);
1251 
1252 				return decode_mouse_event(pmer2);
1253 			    }
1254 			    else if (s_xOldMouse == pmer2->dwMousePosition.X &&
1255 				     s_yOldMouse == pmer2->dwMousePosition.Y)
1256 			    {
1257 				/* throw away spurious mouse move */
1258 				read_console_input(g_hConIn, &ir, 1, &cRecords);
1259 
1260 				/* are there any more mouse events in queue? */
1261 				peek_console_input(g_hConIn, &ir, 1, &cRecords);
1262 
1263 				if (cRecords==0 || ir.EventType != MOUSE_EVENT)
1264 				    break;
1265 			    }
1266 			    else
1267 				break;
1268 			}
1269 		    }
1270 		}
1271 	    }
1272 	}
1273 
1274 	if (s_fNextIsMiddle)
1275 	{
1276 	    nButton = (pmer->dwEventFlags == MOUSE_MOVED)
1277 		? MOUSE_DRAG : MOUSE_MIDDLE;
1278 	    s_fNextIsMiddle = FALSE;
1279 	}
1280 	else if (cButtons == 2	&&
1281 	    ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
1282 	{
1283 	    nButton = MOUSE_MIDDLE;
1284 
1285 	    if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED)
1286 	    {
1287 		s_fNextIsMiddle = TRUE;
1288 		nButton = MOUSE_RELEASE;
1289 	    }
1290 	}
1291 	else if ((pmer->dwButtonState & LEFT) == LEFT)
1292 	    nButton = MOUSE_LEFT;
1293 	else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
1294 	    nButton = MOUSE_MIDDLE;
1295 	else if ((pmer->dwButtonState & RIGHT) == RIGHT)
1296 	    nButton = MOUSE_RIGHT;
1297 
1298 	if (! s_fReleased && ! s_fNextIsMiddle
1299 		&& nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG)
1300 	    return FALSE;
1301 
1302 	s_fReleased = s_fNextIsMiddle;
1303     }
1304 
1305     if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK)
1306     {
1307 	/* button pressed or released, without mouse moving */
1308 	if (nButton != -1 && nButton != MOUSE_RELEASE)
1309 	{
1310 	    DWORD dwCurrentTime = GetTickCount();
1311 
1312 	    if (s_xOldMouse != g_xMouse
1313 		    || s_yOldMouse != g_yMouse
1314 		    || s_nOldButton != nButton
1315 		    || s_old_topline != curwin->w_topline
1316 #ifdef FEAT_DIFF
1317 		    || s_old_topfill != curwin->w_topfill
1318 #endif
1319 		    || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset)
1320 	    {
1321 		s_cClicks = 1;
1322 	    }
1323 	    else if (++s_cClicks > 4)
1324 	    {
1325 		s_cClicks = 1;
1326 	    }
1327 
1328 	    s_dwLastClickTime = dwCurrentTime;
1329 	}
1330     }
1331     else if (pmer->dwEventFlags == MOUSE_MOVED)
1332     {
1333 	if (nButton != -1 && nButton != MOUSE_RELEASE)
1334 	    nButton = MOUSE_DRAG;
1335 
1336 	s_cClicks = 1;
1337     }
1338 
1339     if (nButton == -1)
1340 	return FALSE;
1341 
1342     if (nButton != MOUSE_RELEASE)
1343 	s_nOldButton = nButton;
1344 
1345     g_nMouseClick = nButton;
1346 
1347     if (pmer->dwControlKeyState & SHIFT_PRESSED)
1348 	g_nMouseClick |= MOUSE_SHIFT;
1349     if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
1350 	g_nMouseClick |= MOUSE_CTRL;
1351     if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED  | LEFT_ALT_PRESSED))
1352 	g_nMouseClick |= MOUSE_ALT;
1353 
1354     if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE)
1355 	SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
1356 
1357     /* only pass on interesting (i.e., different) mouse events */
1358     if (s_xOldMouse == g_xMouse
1359 	    && s_yOldMouse == g_yMouse
1360 	    && s_nOldMouseClick == g_nMouseClick)
1361     {
1362 	g_nMouseClick = -1;
1363 	return FALSE;
1364     }
1365 
1366     s_xOldMouse = g_xMouse;
1367     s_yOldMouse = g_yMouse;
1368     s_old_topline = curwin->w_topline;
1369 #ifdef FEAT_DIFF
1370     s_old_topfill = curwin->w_topfill;
1371 #endif
1372     s_nOldMouseClick = g_nMouseClick;
1373 
1374     return TRUE;
1375 }
1376 
1377 # endif /* FEAT_GUI_W32 */
1378 #endif /* FEAT_MOUSE */
1379 
1380 
1381 #ifdef MCH_CURSOR_SHAPE
1382 /*
1383  * Set the shape of the cursor.
1384  * 'thickness' can be from 1 (thin) to 99 (block)
1385  */
1386     static void
1387 mch_set_cursor_shape(int thickness)
1388 {
1389     CONSOLE_CURSOR_INFO ConsoleCursorInfo;
1390     ConsoleCursorInfo.dwSize = thickness;
1391     ConsoleCursorInfo.bVisible = s_cursor_visible;
1392 
1393     SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo);
1394     if (s_cursor_visible)
1395 	SetConsoleCursorPosition(g_hConOut, g_coord);
1396 }
1397 
1398     void
1399 mch_update_cursor(void)
1400 {
1401     int		idx;
1402     int		thickness;
1403 
1404     /*
1405      * How the cursor is drawn depends on the current mode.
1406      */
1407     idx = get_shape_idx(FALSE);
1408 
1409     if (shape_table[idx].shape == SHAPE_BLOCK)
1410 	thickness = 99;	/* 100 doesn't work on W95 */
1411     else
1412 	thickness = shape_table[idx].percentage;
1413     mch_set_cursor_shape(thickness);
1414 }
1415 #endif
1416 
1417 #ifndef FEAT_GUI_W32	    /* this isn't used for the GUI */
1418 /*
1419  * Handle FOCUS_EVENT.
1420  */
1421     static void
1422 handle_focus_event(INPUT_RECORD ir)
1423 {
1424     g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
1425     ui_focus_change((int)g_fJustGotFocus);
1426 }
1427 
1428 /*
1429  * Wait until console input from keyboard or mouse is available,
1430  * or the time is up.
1431  * Return TRUE if something is available FALSE if not.
1432  */
1433     static int
1434 WaitForChar(long msec)
1435 {
1436     DWORD	    dwNow = 0, dwEndTime = 0;
1437     INPUT_RECORD    ir;
1438     DWORD	    cRecords;
1439     WCHAR	    ch, ch2;
1440 
1441     if (msec > 0)
1442 	/* Wait until the specified time has elapsed. */
1443 	dwEndTime = GetTickCount() + msec;
1444     else if (msec < 0)
1445 	/* Wait forever. */
1446 	dwEndTime = INFINITE;
1447 
1448     /* We need to loop until the end of the time period, because
1449      * we might get multiple unusable mouse events in that time.
1450      */
1451     for (;;)
1452     {
1453 #ifdef FEAT_MZSCHEME
1454 	mzvim_check_threads();
1455 #endif
1456 #ifdef FEAT_CLIENTSERVER
1457 	serverProcessPendingMessages();
1458 #endif
1459 	if (0
1460 #ifdef FEAT_MOUSE
1461 		|| g_nMouseClick != -1
1462 #endif
1463 #ifdef FEAT_CLIENTSERVER
1464 		|| input_available()
1465 #endif
1466 	   )
1467 	    return TRUE;
1468 
1469 	if (msec > 0)
1470 	{
1471 	    /* If the specified wait time has passed, return.  Beware that
1472 	     * GetTickCount() may wrap around (overflow). */
1473 	    dwNow = GetTickCount();
1474 	    if ((int)(dwNow - dwEndTime) >= 0)
1475 		break;
1476 	}
1477 	if (msec != 0)
1478 	{
1479 	    DWORD dwWaitTime = dwEndTime - dwNow;
1480 
1481 #ifdef FEAT_MZSCHEME
1482 	    if (mzthreads_allowed() && p_mzq > 0
1483 				    && (msec < 0 || (long)dwWaitTime > p_mzq))
1484 		dwWaitTime = p_mzq; /* don't wait longer than 'mzquantum' */
1485 #endif
1486 #ifdef FEAT_CLIENTSERVER
1487 	    /* Wait for either an event on the console input or a message in
1488 	     * the client-server window. */
1489 	    if (msg_wait_for_multiple_objects(1, &g_hConIn, FALSE,
1490 				 dwWaitTime, QS_SENDMESSAGE) != WAIT_OBJECT_0)
1491 #else
1492 	    if (wait_for_single_object(g_hConIn, dwWaitTime) != WAIT_OBJECT_0)
1493 #endif
1494 		    continue;
1495 	}
1496 
1497 	cRecords = 0;
1498 	peek_console_input(g_hConIn, &ir, 1, &cRecords);
1499 
1500 #ifdef FEAT_MBYTE_IME
1501 	if (State & CMDLINE && msg_row == Rows - 1)
1502 	{
1503 	    CONSOLE_SCREEN_BUFFER_INFO csbi;
1504 
1505 	    if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1506 	    {
1507 		if (csbi.dwCursorPosition.Y != msg_row)
1508 		{
1509 		    /* The screen is now messed up, must redraw the
1510 		     * command line and later all the windows. */
1511 		    redraw_all_later(CLEAR);
1512 		    cmdline_row -= (msg_row - csbi.dwCursorPosition.Y);
1513 		    redrawcmd();
1514 		}
1515 	    }
1516 	}
1517 #endif
1518 
1519 	if (cRecords > 0)
1520 	{
1521 	    if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
1522 	    {
1523 #ifdef FEAT_MBYTE_IME
1524 		/* Windows IME sends two '\n's with only one 'ENTER'.  First:
1525 		 * wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */
1526 		if (ir.Event.KeyEvent.UChar == 0
1527 			&& ir.Event.KeyEvent.wVirtualKeyCode == 13)
1528 		{
1529 		    read_console_input(g_hConIn, &ir, 1, &cRecords);
1530 		    continue;
1531 		}
1532 #endif
1533 		if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
1534 								 NULL, FALSE))
1535 		    return TRUE;
1536 	    }
1537 
1538 	    read_console_input(g_hConIn, &ir, 1, &cRecords);
1539 
1540 	    if (ir.EventType == FOCUS_EVENT)
1541 		handle_focus_event(ir);
1542 	    else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1543 		shell_resized();
1544 #ifdef FEAT_MOUSE
1545 	    else if (ir.EventType == MOUSE_EVENT
1546 		    && decode_mouse_event(&ir.Event.MouseEvent))
1547 		return TRUE;
1548 #endif
1549 	}
1550 	else if (msec == 0)
1551 	    break;
1552     }
1553 
1554 #ifdef FEAT_CLIENTSERVER
1555     /* Something might have been received while we were waiting. */
1556     if (input_available())
1557 	return TRUE;
1558 #endif
1559     return FALSE;
1560 }
1561 
1562 #ifndef FEAT_GUI_MSWIN
1563 /*
1564  * return non-zero if a character is available
1565  */
1566     int
1567 mch_char_avail(void)
1568 {
1569     return WaitForChar(0L);
1570 }
1571 #endif
1572 
1573 /*
1574  * Create the console input.  Used when reading stdin doesn't work.
1575  */
1576     static void
1577 create_conin(void)
1578 {
1579     g_hConIn =	CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
1580 			FILE_SHARE_READ|FILE_SHARE_WRITE,
1581 			(LPSECURITY_ATTRIBUTES) NULL,
1582 			OPEN_EXISTING, 0, (HANDLE)NULL);
1583     did_create_conin = TRUE;
1584 }
1585 
1586 /*
1587  * Get a keystroke or a mouse event
1588  */
1589     static WCHAR
1590 tgetch(int *pmodifiers, WCHAR *pch2)
1591 {
1592     WCHAR ch;
1593 
1594     for (;;)
1595     {
1596 	INPUT_RECORD ir;
1597 	DWORD cRecords = 0;
1598 
1599 #ifdef FEAT_CLIENTSERVER
1600 	(void)WaitForChar(-1L);
1601 	if (input_available())
1602 	    return 0;
1603 # ifdef FEAT_MOUSE
1604 	if (g_nMouseClick != -1)
1605 	    return 0;
1606 # endif
1607 #endif
1608 	if (read_console_input(g_hConIn, &ir, 1, &cRecords) == 0)
1609 	{
1610 	    if (did_create_conin)
1611 		read_error_exit();
1612 	    create_conin();
1613 	    continue;
1614 	}
1615 
1616 	if (ir.EventType == KEY_EVENT)
1617 	{
1618 	    if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2,
1619 							    pmodifiers, TRUE))
1620 		return ch;
1621 	}
1622 	else if (ir.EventType == FOCUS_EVENT)
1623 	    handle_focus_event(ir);
1624 	else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1625 	    shell_resized();
1626 #ifdef FEAT_MOUSE
1627 	else if (ir.EventType == MOUSE_EVENT)
1628 	{
1629 	    if (decode_mouse_event(&ir.Event.MouseEvent))
1630 		return 0;
1631 	}
1632 #endif
1633     }
1634 }
1635 #endif /* !FEAT_GUI_W32 */
1636 
1637 
1638 /*
1639  * mch_inchar(): low-level input function.
1640  * Get one or more characters from the keyboard or the mouse.
1641  * If time == 0, do not wait for characters.
1642  * If time == n, wait a short time for characters.
1643  * If time == -1, wait forever for characters.
1644  * Returns the number of characters read into buf.
1645  */
1646 /*ARGSUSED*/
1647     int
1648 mch_inchar(
1649     char_u	*buf,
1650     int		maxlen,
1651     long	time,
1652     int		tb_change_cnt)
1653 {
1654 #ifndef FEAT_GUI_W32	    /* this isn't used for the GUI */
1655 
1656     int		len;
1657     int		c;
1658 #define TYPEAHEADLEN 20
1659     static char_u   typeahead[TYPEAHEADLEN];	/* previously typed bytes. */
1660     static int	    typeaheadlen = 0;
1661 
1662     /* First use any typeahead that was kept because "buf" was too small. */
1663     if (typeaheadlen > 0)
1664 	goto theend;
1665 
1666 #ifdef FEAT_SNIFF
1667     if (want_sniff_request)
1668     {
1669 	if (sniff_request_waiting)
1670 	{
1671 	    /* return K_SNIFF */
1672 	    typeahead[typeaheadlen++] = CSI;
1673 	    typeahead[typeaheadlen++] = (char_u)KS_EXTRA;
1674 	    typeahead[typeaheadlen++] = (char_u)KE_SNIFF;
1675 	    sniff_request_waiting = 0;
1676 	    want_sniff_request = 0;
1677 	    goto theend;
1678 	}
1679 	else if (time < 0 || time > 250)
1680 	{
1681 	    /* don't wait too long, a request might be pending */
1682 	    time = 250;
1683 	}
1684     }
1685 #endif
1686 
1687     if (time >= 0)
1688     {
1689 	if (!WaitForChar(time))     /* no character available */
1690 	    return 0;
1691     }
1692     else    /* time == -1, wait forever */
1693     {
1694 	mch_set_winsize_now();	/* Allow winsize changes from now on */
1695 
1696 	/*
1697 	 * If there is no character available within 2 seconds (default)
1698 	 * write the autoscript file to disk.  Or cause the CursorHold event
1699 	 * to be triggered.
1700 	 */
1701 	if (!WaitForChar(p_ut))
1702 	{
1703 #ifdef FEAT_AUTOCMD
1704 	    if (trigger_cursorhold() && maxlen >= 3)
1705 	    {
1706 		buf[0] = K_SPECIAL;
1707 		buf[1] = KS_EXTRA;
1708 		buf[2] = (int)KE_CURSORHOLD;
1709 		return 3;
1710 	    }
1711 #endif
1712 	    before_blocking();
1713 	}
1714     }
1715 
1716     /*
1717      * Try to read as many characters as there are, until the buffer is full.
1718      */
1719 
1720     /* we will get at least one key. Get more if they are available. */
1721     g_fCBrkPressed = FALSE;
1722 
1723 #ifdef MCH_WRITE_DUMP
1724     if (fdDump)
1725 	fputc('[', fdDump);
1726 #endif
1727 
1728     /* Keep looping until there is something in the typeahead buffer and more
1729      * to get and still room in the buffer (up to two bytes for a char and
1730      * three bytes for a modifier). */
1731     while ((typeaheadlen == 0 || WaitForChar(0L))
1732 					  && typeaheadlen + 5 <= TYPEAHEADLEN)
1733     {
1734 	if (typebuf_changed(tb_change_cnt))
1735 	{
1736 	    /* "buf" may be invalid now if a client put something in the
1737 	     * typeahead buffer and "buf" is in the typeahead buffer. */
1738 	    typeaheadlen = 0;
1739 	    break;
1740 	}
1741 #ifdef FEAT_MOUSE
1742 	if (g_nMouseClick != -1)
1743 	{
1744 # ifdef MCH_WRITE_DUMP
1745 	    if (fdDump)
1746 		fprintf(fdDump, "{%02x @ %d, %d}",
1747 			g_nMouseClick, g_xMouse, g_yMouse);
1748 # endif
1749 	    typeahead[typeaheadlen++] = ESC + 128;
1750 	    typeahead[typeaheadlen++] = 'M';
1751 	    typeahead[typeaheadlen++] = g_nMouseClick;
1752 	    typeahead[typeaheadlen++] = g_xMouse + '!';
1753 	    typeahead[typeaheadlen++] = g_yMouse + '!';
1754 	    g_nMouseClick = -1;
1755 	}
1756 	else
1757 #endif
1758 	{
1759 	    WCHAR	ch2 = NUL;
1760 	    int		modifiers = 0;
1761 
1762 	    c = tgetch(&modifiers, &ch2);
1763 
1764 	    if (typebuf_changed(tb_change_cnt))
1765 	    {
1766 		/* "buf" may be invalid now if a client put something in the
1767 		 * typeahead buffer and "buf" is in the typeahead buffer. */
1768 		typeaheadlen = 0;
1769 		break;
1770 	    }
1771 
1772 	    if (c == Ctrl_C && ctrl_c_interrupts)
1773 	    {
1774 #if defined(FEAT_CLIENTSERVER)
1775 		trash_input_buf();
1776 #endif
1777 		got_int = TRUE;
1778 	    }
1779 
1780 #ifdef FEAT_MOUSE
1781 	    if (g_nMouseClick == -1)
1782 #endif
1783 	    {
1784 		int	n = 1;
1785 		int     conv = FALSE;
1786 
1787 #ifdef FEAT_MBYTE
1788 		if (ch2 == NUL)
1789 		{
1790 		    int	    i;
1791 		    char_u  *p;
1792 		    WCHAR   ch[2];
1793 
1794 		    ch[0] = c;
1795 		    if (c >= 0xD800 && c <= 0xDBFF)	/* High surrogate */
1796 		    {
1797 			ch[1] = tgetch(&modifiers, &ch2);
1798 			n++;
1799 		    }
1800 		    p = utf16_to_enc(ch, &n);
1801 		    if (p != NULL)
1802 		    {
1803 			for (i = 0; i < n; i++)
1804 			    typeahead[typeaheadlen + i] = p[i];
1805 			vim_free(p);
1806 		    }
1807 		}
1808 		else
1809 #endif
1810 		    typeahead[typeaheadlen] = c;
1811 		if (ch2 != NUL)
1812 		{
1813 		    typeahead[typeaheadlen + n] = 3;
1814 		    typeahead[typeaheadlen + n + 1] = (char_u)ch2;
1815 		    n += 2;
1816 		}
1817 
1818 		if (conv)
1819 		{
1820 		    char_u *p = typeahead + typeaheadlen;
1821 
1822 		    if (*p != K_NUL)
1823 		    {
1824 			char_u *e = typeahead + TYPEAHEADLEN;
1825 
1826 			while (*p && p < e)
1827 			{
1828 			    if (*p == K_NUL)
1829 			    {
1830 				++p;
1831 				mch_memmove(p + 1, p, ((size_t)(e - p)) - 1);
1832 				*p = 3;
1833 				++n;
1834 			    }
1835 			    ++p;
1836 			}
1837 		    }
1838 		}
1839 
1840 		/* Use the ALT key to set the 8th bit of the character
1841 		 * when it's one byte, the 8th bit isn't set yet and not
1842 		 * using a double-byte encoding (would become a lead
1843 		 * byte). */
1844 		if ((modifiers & MOD_MASK_ALT)
1845 			&& n == 1
1846 			&& (typeahead[typeaheadlen] & 0x80) == 0
1847 #ifdef FEAT_MBYTE
1848 			&& !enc_dbcs
1849 #endif
1850 		   )
1851 		{
1852 #ifdef FEAT_MBYTE
1853 		    n = (*mb_char2bytes)(typeahead[typeaheadlen] | 0x80,
1854 						    typeahead + typeaheadlen);
1855 #else
1856 		    typeahead[typeaheadlen] |= 0x80;
1857 #endif
1858 		    modifiers &= ~MOD_MASK_ALT;
1859 		}
1860 
1861 		if (modifiers != 0)
1862 		{
1863 		    /* Prepend modifiers to the character. */
1864 		    mch_memmove(typeahead + typeaheadlen + 3,
1865 						 typeahead + typeaheadlen, n);
1866 		    typeahead[typeaheadlen++] = K_SPECIAL;
1867 		    typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
1868 		    typeahead[typeaheadlen++] =  modifiers;
1869 		}
1870 
1871 		typeaheadlen += n;
1872 
1873 #ifdef MCH_WRITE_DUMP
1874 		if (fdDump)
1875 		    fputc(c, fdDump);
1876 #endif
1877 	    }
1878 	}
1879     }
1880 
1881 #ifdef MCH_WRITE_DUMP
1882     if (fdDump)
1883     {
1884 	fputs("]\n", fdDump);
1885 	fflush(fdDump);
1886     }
1887 #endif
1888 
1889 theend:
1890     /* Move typeahead to "buf", as much as fits. */
1891     len = 0;
1892     while (len < maxlen && typeaheadlen > 0)
1893     {
1894 	buf[len++] = typeahead[0];
1895 	mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
1896     }
1897     return len;
1898 
1899 #else /* FEAT_GUI_W32 */
1900     return 0;
1901 #endif /* FEAT_GUI_W32 */
1902 }
1903 
1904 #ifndef PROTO
1905 # ifndef __MINGW32__
1906 #  include <shellapi.h>	/* required for FindExecutable() */
1907 # endif
1908 #endif
1909 
1910 /*
1911  * Return TRUE if "name" is in $PATH.
1912  * TODO: Should somehow check if it's really executable.
1913  */
1914     static int
1915 executable_exists(char *name, char_u **path)
1916 {
1917     char	*dum;
1918     char	fname[_MAX_PATH];
1919     char	*curpath, *newpath;
1920     long	n;
1921 
1922 #ifdef FEAT_MBYTE
1923     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
1924     {
1925 	WCHAR	*p = enc_to_utf16(name, NULL);
1926 	WCHAR	fnamew[_MAX_PATH];
1927 	WCHAR	*dumw;
1928 	WCHAR	*wcurpath, *wnewpath;
1929 
1930 	if (p != NULL)
1931 	{
1932 	    wcurpath = _wgetenv(L"PATH");
1933 	    wnewpath = (WCHAR*)alloc((unsigned)(wcslen(wcurpath) + 3)
1934 							    * sizeof(WCHAR));
1935 	    if (wnewpath == NULL)
1936 		return FALSE;
1937 	    wcscpy(wnewpath, L".;");
1938 	    wcscat(wnewpath, wcurpath);
1939 	    n = (long)SearchPathW(wnewpath, p, NULL, _MAX_PATH, fnamew, &dumw);
1940 	    vim_free(wnewpath);
1941 	    vim_free(p);
1942 	    if (n > 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
1943 	    {
1944 		if (n == 0)
1945 		    return FALSE;
1946 		if (GetFileAttributesW(fnamew) & FILE_ATTRIBUTE_DIRECTORY)
1947 		    return FALSE;
1948 		if (path != NULL)
1949 		    *path = utf16_to_enc(fnamew, NULL);
1950 		return TRUE;
1951 	    }
1952 	    /* Retry with non-wide function (for Windows 98). */
1953 	}
1954     }
1955 #endif
1956 
1957     curpath = getenv("PATH");
1958     newpath = (char*)alloc((unsigned)(STRLEN(curpath) + 3));
1959     if (newpath == NULL)
1960 	return FALSE;
1961     STRCPY(newpath, ".;");
1962     STRCAT(newpath, curpath);
1963     n = (long)SearchPath(newpath, name, NULL, _MAX_PATH, fname, &dum);
1964     vim_free(newpath);
1965     if (n == 0)
1966 	return FALSE;
1967     if (mch_isdir(fname))
1968 	return FALSE;
1969     if (path != NULL)
1970 	*path = vim_strsave(fname);
1971     return TRUE;
1972 }
1973 
1974 #if ((defined(__MINGW32__) || defined (__CYGWIN32__)) && \
1975        __MSVCRT_VERSION__ >= 0x800) || (defined(_MSC_VER) && _MSC_VER >= 1400)
1976 /*
1977  * Bad parameter handler.
1978  *
1979  * Certain MS CRT functions will intentionally crash when passed invalid
1980  * parameters to highlight possible security holes.  Setting this function as
1981  * the bad parameter handler will prevent the crash.
1982  *
1983  * In debug builds the parameters contain CRT information that might help track
1984  * down the source of a problem, but in non-debug builds the arguments are all
1985  * NULL/0.  Debug builds will also produce assert dialogs from the CRT, it is
1986  * worth allowing these to make debugging of issues easier.
1987  */
1988     static void
1989 bad_param_handler(const wchar_t *expression,
1990     const wchar_t *function,
1991     const wchar_t *file,
1992     unsigned int line,
1993     uintptr_t pReserved)
1994 {
1995 }
1996 
1997 # define SET_INVALID_PARAM_HANDLER \
1998 	((void)_set_invalid_parameter_handler(bad_param_handler))
1999 #else
2000 # define SET_INVALID_PARAM_HANDLER
2001 #endif
2002 
2003 #ifdef FEAT_GUI_W32
2004 
2005 /*
2006  * GUI version of mch_init().
2007  */
2008     void
2009 mch_init(void)
2010 {
2011 #ifndef __MINGW32__
2012     extern int _fmode;
2013 #endif
2014 
2015     /* Silently handle invalid parameters to CRT functions */
2016     SET_INVALID_PARAM_HANDLER;
2017 
2018     /* Let critical errors result in a failure, not in a dialog box.  Required
2019      * for the timestamp test to work on removed floppies. */
2020     SetErrorMode(SEM_FAILCRITICALERRORS);
2021 
2022     _fmode = O_BINARY;		/* we do our own CR-LF translation */
2023 
2024     /* Specify window size.  Is there a place to get the default from? */
2025     Rows = 25;
2026     Columns = 80;
2027 
2028     /* Look for 'vimrun' */
2029     if (!gui_is_win32s())
2030     {
2031 	char_u vimrun_location[_MAX_PATH + 4];
2032 
2033 	/* First try in same directory as gvim.exe */
2034 	STRCPY(vimrun_location, exe_name);
2035 	STRCPY(gettail(vimrun_location), "vimrun.exe");
2036 	if (mch_getperm(vimrun_location) >= 0)
2037 	{
2038 	    if (*skiptowhite(vimrun_location) != NUL)
2039 	    {
2040 		/* Enclose path with white space in double quotes. */
2041 		mch_memmove(vimrun_location + 1, vimrun_location,
2042 						 STRLEN(vimrun_location) + 1);
2043 		*vimrun_location = '"';
2044 		STRCPY(gettail(vimrun_location), "vimrun\" ");
2045 	    }
2046 	    else
2047 		STRCPY(gettail(vimrun_location), "vimrun ");
2048 
2049 	    vimrun_path = (char *)vim_strsave(vimrun_location);
2050 	    s_dont_use_vimrun = FALSE;
2051 	}
2052 	else if (executable_exists("vimrun.exe", NULL))
2053 	    s_dont_use_vimrun = FALSE;
2054 
2055 	/* Don't give the warning for a missing vimrun.exe right now, but only
2056 	 * when vimrun was supposed to be used.  Don't bother people that do
2057 	 * not need vimrun.exe. */
2058 	if (s_dont_use_vimrun)
2059 	    need_vimrun_warning = TRUE;
2060     }
2061 
2062     /*
2063      * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
2064      * Otherwise the default "findstr /n" is used.
2065      */
2066     if (!executable_exists("findstr.exe", NULL))
2067 	set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
2068 
2069 #ifdef FEAT_CLIPBOARD
2070     win_clip_init();
2071 #endif
2072 }
2073 
2074 
2075 #else /* FEAT_GUI_W32 */
2076 
2077 #define SRWIDTH(sr) ((sr).Right - (sr).Left + 1)
2078 #define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1)
2079 
2080 /*
2081  * ClearConsoleBuffer()
2082  * Description:
2083  *  Clears the entire contents of the console screen buffer, using the
2084  *  specified attribute.
2085  * Returns:
2086  *  TRUE on success
2087  */
2088     static BOOL
2089 ClearConsoleBuffer(WORD wAttribute)
2090 {
2091     CONSOLE_SCREEN_BUFFER_INFO csbi;
2092     COORD coord;
2093     DWORD NumCells, dummy;
2094 
2095     if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2096 	return FALSE;
2097 
2098     NumCells = csbi.dwSize.X * csbi.dwSize.Y;
2099     coord.X = 0;
2100     coord.Y = 0;
2101     if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells,
2102 	    coord, &dummy))
2103     {
2104 	return FALSE;
2105     }
2106     if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells,
2107 	    coord, &dummy))
2108     {
2109 	return FALSE;
2110     }
2111 
2112     return TRUE;
2113 }
2114 
2115 /*
2116  * FitConsoleWindow()
2117  * Description:
2118  *  Checks if the console window will fit within given buffer dimensions.
2119  *  Also, if requested, will shrink the window to fit.
2120  * Returns:
2121  *  TRUE on success
2122  */
2123     static BOOL
2124 FitConsoleWindow(
2125     COORD dwBufferSize,
2126     BOOL WantAdjust)
2127 {
2128     CONSOLE_SCREEN_BUFFER_INFO csbi;
2129     COORD dwWindowSize;
2130     BOOL NeedAdjust = FALSE;
2131 
2132     if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2133     {
2134 	/*
2135 	 * A buffer resize will fail if the current console window does
2136 	 * not lie completely within that buffer.  To avoid this, we might
2137 	 * have to move and possibly shrink the window.
2138 	 */
2139 	if (csbi.srWindow.Right >= dwBufferSize.X)
2140 	{
2141 	    dwWindowSize.X = SRWIDTH(csbi.srWindow);
2142 	    if (dwWindowSize.X > dwBufferSize.X)
2143 		dwWindowSize.X = dwBufferSize.X;
2144 	    csbi.srWindow.Right = dwBufferSize.X - 1;
2145 	    csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X;
2146 	    NeedAdjust = TRUE;
2147 	}
2148 	if (csbi.srWindow.Bottom >= dwBufferSize.Y)
2149 	{
2150 	    dwWindowSize.Y = SRHEIGHT(csbi.srWindow);
2151 	    if (dwWindowSize.Y > dwBufferSize.Y)
2152 		dwWindowSize.Y = dwBufferSize.Y;
2153 	    csbi.srWindow.Bottom = dwBufferSize.Y - 1;
2154 	    csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y;
2155 	    NeedAdjust = TRUE;
2156 	}
2157 	if (NeedAdjust && WantAdjust)
2158 	{
2159 	    if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow))
2160 		return FALSE;
2161 	}
2162 	return TRUE;
2163     }
2164 
2165     return FALSE;
2166 }
2167 
2168 typedef struct ConsoleBufferStruct
2169 {
2170     BOOL			IsValid;
2171     CONSOLE_SCREEN_BUFFER_INFO	Info;
2172     PCHAR_INFO			Buffer;
2173     COORD			BufferSize;
2174 } ConsoleBuffer;
2175 
2176 /*
2177  * SaveConsoleBuffer()
2178  * Description:
2179  *  Saves important information about the console buffer, including the
2180  *  actual buffer contents.  The saved information is suitable for later
2181  *  restoration by RestoreConsoleBuffer().
2182  * Returns:
2183  *  TRUE if all information was saved; FALSE otherwise
2184  *  If FALSE, still sets cb->IsValid if buffer characteristics were saved.
2185  */
2186     static BOOL
2187 SaveConsoleBuffer(
2188     ConsoleBuffer *cb)
2189 {
2190     DWORD NumCells;
2191     COORD BufferCoord;
2192     SMALL_RECT ReadRegion;
2193     WORD Y, Y_incr;
2194 
2195     if (cb == NULL)
2196 	return FALSE;
2197 
2198     if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
2199     {
2200 	cb->IsValid = FALSE;
2201 	return FALSE;
2202     }
2203     cb->IsValid = TRUE;
2204 
2205     /*
2206      * Allocate a buffer large enough to hold the entire console screen
2207      * buffer.  If this ConsoleBuffer structure has already been initialized
2208      * with a buffer of the correct size, then just use that one.
2209      */
2210     if (!cb->IsValid || cb->Buffer == NULL ||
2211 	    cb->BufferSize.X != cb->Info.dwSize.X ||
2212 	    cb->BufferSize.Y != cb->Info.dwSize.Y)
2213     {
2214 	cb->BufferSize.X = cb->Info.dwSize.X;
2215 	cb->BufferSize.Y = cb->Info.dwSize.Y;
2216 	NumCells = cb->BufferSize.X * cb->BufferSize.Y;
2217 	vim_free(cb->Buffer);
2218 	cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
2219 	if (cb->Buffer == NULL)
2220 	    return FALSE;
2221     }
2222 
2223     /*
2224      * We will now copy the console screen buffer into our buffer.
2225      * ReadConsoleOutput() seems to be limited as far as how much you
2226      * can read at a time.  Empirically, this number seems to be about
2227      * 12000 cells (rows * columns).  Start at position (0, 0) and copy
2228      * in chunks until it is all copied.  The chunks will all have the
2229      * same horizontal characteristics, so initialize them now.  The
2230      * height of each chunk will be (12000 / width).
2231      */
2232     BufferCoord.X = 0;
2233     ReadRegion.Left = 0;
2234     ReadRegion.Right = cb->Info.dwSize.X - 1;
2235     Y_incr = 12000 / cb->Info.dwSize.X;
2236     for (Y = 0; Y < cb->BufferSize.Y; Y += Y_incr)
2237     {
2238 	/*
2239 	 * Read into position (0, Y) in our buffer.
2240 	 */
2241 	BufferCoord.Y = Y;
2242 	/*
2243 	 * Read the region whose top left corner is (0, Y) and whose bottom
2244 	 * right corner is (width - 1, Y + Y_incr - 1).  This should define
2245 	 * a region of size width by Y_incr.  Don't worry if this region is
2246 	 * too large for the remaining buffer; it will be cropped.
2247 	 */
2248 	ReadRegion.Top = Y;
2249 	ReadRegion.Bottom = Y + Y_incr - 1;
2250 	if (!ReadConsoleOutput(g_hConOut,	/* output handle */
2251 		cb->Buffer,			/* our buffer */
2252 		cb->BufferSize,			/* dimensions of our buffer */
2253 		BufferCoord,			/* offset in our buffer */
2254 		&ReadRegion))			/* region to save */
2255 	{
2256 	    vim_free(cb->Buffer);
2257 	    cb->Buffer = NULL;
2258 	    return FALSE;
2259 	}
2260     }
2261 
2262     return TRUE;
2263 }
2264 
2265 /*
2266  * RestoreConsoleBuffer()
2267  * Description:
2268  *  Restores important information about the console buffer, including the
2269  *  actual buffer contents, if desired.  The information to restore is in
2270  *  the same format used by SaveConsoleBuffer().
2271  * Returns:
2272  *  TRUE on success
2273  */
2274     static BOOL
2275 RestoreConsoleBuffer(
2276     ConsoleBuffer   *cb,
2277     BOOL	    RestoreScreen)
2278 {
2279     COORD BufferCoord;
2280     SMALL_RECT WriteRegion;
2281 
2282     if (cb == NULL || !cb->IsValid)
2283 	return FALSE;
2284 
2285     /*
2286      * Before restoring the buffer contents, clear the current buffer, and
2287      * restore the cursor position and window information.  Doing this now
2288      * prevents old buffer contents from "flashing" onto the screen.
2289      */
2290     if (RestoreScreen)
2291 	ClearConsoleBuffer(cb->Info.wAttributes);
2292 
2293     FitConsoleWindow(cb->Info.dwSize, TRUE);
2294     if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
2295 	return FALSE;
2296     if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
2297 	return FALSE;
2298 
2299     if (!RestoreScreen)
2300     {
2301 	/*
2302 	 * No need to restore the screen buffer contents, so we're done.
2303 	 */
2304 	return TRUE;
2305     }
2306 
2307     if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
2308 	return FALSE;
2309     if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
2310 	return FALSE;
2311 
2312     /*
2313      * Restore the screen buffer contents.
2314      */
2315     if (cb->Buffer != NULL)
2316     {
2317 	BufferCoord.X = 0;
2318 	BufferCoord.Y = 0;
2319 	WriteRegion.Left = 0;
2320 	WriteRegion.Top = 0;
2321 	WriteRegion.Right = cb->Info.dwSize.X - 1;
2322 	WriteRegion.Bottom = cb->Info.dwSize.Y - 1;
2323 	if (!WriteConsoleOutput(g_hConOut,	/* output handle */
2324 		cb->Buffer,			/* our buffer */
2325 		cb->BufferSize,			/* dimensions of our buffer */
2326 		BufferCoord,			/* offset in our buffer */
2327 		&WriteRegion))			/* region to restore */
2328 	{
2329 	    return FALSE;
2330 	}
2331     }
2332 
2333     return TRUE;
2334 }
2335 
2336 #define FEAT_RESTORE_ORIG_SCREEN
2337 #ifdef FEAT_RESTORE_ORIG_SCREEN
2338 static ConsoleBuffer g_cbOrig = { 0 };
2339 #endif
2340 static ConsoleBuffer g_cbNonTermcap = { 0 };
2341 static ConsoleBuffer g_cbTermcap = { 0 };
2342 
2343 #ifdef FEAT_TITLE
2344 #ifdef __BORLANDC__
2345 typedef HWND (__stdcall *GETCONSOLEWINDOWPROC)(VOID);
2346 #else
2347 typedef WINBASEAPI HWND (WINAPI *GETCONSOLEWINDOWPROC)(VOID);
2348 #endif
2349 char g_szOrigTitle[256] = { 0 };
2350 HWND g_hWnd = NULL;	/* also used in os_mswin.c */
2351 static HICON g_hOrigIconSmall = NULL;
2352 static HICON g_hOrigIcon = NULL;
2353 static HICON g_hVimIcon = NULL;
2354 static BOOL g_fCanChangeIcon = FALSE;
2355 
2356 /* ICON* are not defined in VC++ 4.0 */
2357 #ifndef ICON_SMALL
2358 #define ICON_SMALL 0
2359 #endif
2360 #ifndef ICON_BIG
2361 #define ICON_BIG 1
2362 #endif
2363 /*
2364  * GetConsoleIcon()
2365  * Description:
2366  *  Attempts to retrieve the small icon and/or the big icon currently in
2367  *  use by a given window.
2368  * Returns:
2369  *  TRUE on success
2370  */
2371     static BOOL
2372 GetConsoleIcon(
2373     HWND	hWnd,
2374     HICON	*phIconSmall,
2375     HICON	*phIcon)
2376 {
2377     if (hWnd == NULL)
2378 	return FALSE;
2379 
2380     if (phIconSmall != NULL)
2381 	*phIconSmall = (HICON)SendMessage(hWnd, WM_GETICON,
2382 					       (WPARAM)ICON_SMALL, (LPARAM)0);
2383     if (phIcon != NULL)
2384 	*phIcon = (HICON)SendMessage(hWnd, WM_GETICON,
2385 						 (WPARAM)ICON_BIG, (LPARAM)0);
2386     return TRUE;
2387 }
2388 
2389 /*
2390  * SetConsoleIcon()
2391  * Description:
2392  *  Attempts to change the small icon and/or the big icon currently in
2393  *  use by a given window.
2394  * Returns:
2395  *  TRUE on success
2396  */
2397     static BOOL
2398 SetConsoleIcon(
2399     HWND    hWnd,
2400     HICON   hIconSmall,
2401     HICON   hIcon)
2402 {
2403     HICON   hPrevIconSmall;
2404     HICON   hPrevIcon;
2405 
2406     if (hWnd == NULL)
2407 	return FALSE;
2408 
2409     if (hIconSmall != NULL)
2410 	hPrevIconSmall = (HICON)SendMessage(hWnd, WM_SETICON,
2411 				      (WPARAM)ICON_SMALL, (LPARAM)hIconSmall);
2412     if (hIcon != NULL)
2413 	hPrevIcon = (HICON)SendMessage(hWnd, WM_SETICON,
2414 					     (WPARAM)ICON_BIG,(LPARAM) hIcon);
2415     return TRUE;
2416 }
2417 
2418 /*
2419  * SaveConsoleTitleAndIcon()
2420  * Description:
2421  *  Saves the current console window title in g_szOrigTitle, for later
2422  *  restoration.  Also, attempts to obtain a handle to the console window,
2423  *  and use it to save the small and big icons currently in use by the
2424  *  console window.  This is not always possible on some versions of Windows;
2425  *  nor is it possible when running Vim remotely using Telnet (since the
2426  *  console window the user sees is owned by a remote process).
2427  */
2428     static void
2429 SaveConsoleTitleAndIcon(void)
2430 {
2431     GETCONSOLEWINDOWPROC GetConsoleWindowProc;
2432 
2433     /* Save the original title. */
2434     if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle)))
2435 	return;
2436 
2437     /*
2438      * Obtain a handle to the console window using GetConsoleWindow() from
2439      * KERNEL32.DLL; we need to handle in order to change the window icon.
2440      * This function only exists on NT-based Windows, starting with Windows
2441      * 2000.  On older operating systems, we can't change the window icon
2442      * anyway.
2443      */
2444     if ((GetConsoleWindowProc = (GETCONSOLEWINDOWPROC)
2445 	    GetProcAddress(GetModuleHandle("KERNEL32.DLL"),
2446 		    "GetConsoleWindow")) != NULL)
2447     {
2448 	g_hWnd = (*GetConsoleWindowProc)();
2449     }
2450     if (g_hWnd == NULL)
2451 	return;
2452 
2453     /* Save the original console window icon. */
2454     GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon);
2455     if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL)
2456 	return;
2457 
2458     /* Extract the first icon contained in the Vim executable. */
2459     if (mch_icon_load((HANDLE *)&g_hVimIcon) == FAIL || g_hVimIcon == NULL)
2460 	g_hVimIcon = ExtractIcon(NULL, exe_name, 0);
2461     if (g_hVimIcon != NULL)
2462 	g_fCanChangeIcon = TRUE;
2463 }
2464 #endif
2465 
2466 static int g_fWindInitCalled = FALSE;
2467 static int g_fTermcapMode = FALSE;
2468 static CONSOLE_CURSOR_INFO g_cci;
2469 static DWORD g_cmodein = 0;
2470 static DWORD g_cmodeout = 0;
2471 
2472 /*
2473  * non-GUI version of mch_init().
2474  */
2475     void
2476 mch_init(void)
2477 {
2478 #ifndef FEAT_RESTORE_ORIG_SCREEN
2479     CONSOLE_SCREEN_BUFFER_INFO csbi;
2480 #endif
2481 #ifndef __MINGW32__
2482     extern int _fmode;
2483 #endif
2484 
2485     /* Silently handle invalid parameters to CRT functions */
2486     SET_INVALID_PARAM_HANDLER;
2487 
2488     /* Let critical errors result in a failure, not in a dialog box.  Required
2489      * for the timestamp test to work on removed floppies. */
2490     SetErrorMode(SEM_FAILCRITICALERRORS);
2491 
2492     _fmode = O_BINARY;		/* we do our own CR-LF translation */
2493     out_flush();
2494 
2495     /* Obtain handles for the standard Console I/O devices */
2496     if (read_cmd_fd == 0)
2497 	g_hConIn =  GetStdHandle(STD_INPUT_HANDLE);
2498     else
2499 	create_conin();
2500     g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
2501 
2502 #ifdef FEAT_RESTORE_ORIG_SCREEN
2503     /* Save the initial console buffer for later restoration */
2504     SaveConsoleBuffer(&g_cbOrig);
2505     g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
2506 #else
2507     /* Get current text attributes */
2508     GetConsoleScreenBufferInfo(g_hConOut, &csbi);
2509     g_attrCurrent = g_attrDefault = csbi.wAttributes;
2510 #endif
2511     if (cterm_normal_fg_color == 0)
2512 	cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
2513     if (cterm_normal_bg_color == 0)
2514 	cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
2515 
2516     /* set termcap codes to current text attributes */
2517     update_tcap(g_attrCurrent);
2518 
2519     GetConsoleCursorInfo(g_hConOut, &g_cci);
2520     GetConsoleMode(g_hConIn,  &g_cmodein);
2521     GetConsoleMode(g_hConOut, &g_cmodeout);
2522 
2523 #ifdef FEAT_TITLE
2524     SaveConsoleTitleAndIcon();
2525     /*
2526      * Set both the small and big icons of the console window to Vim's icon.
2527      * Note that Vim presently only has one size of icon (32x32), but it
2528      * automatically gets scaled down to 16x16 when setting the small icon.
2529      */
2530     if (g_fCanChangeIcon)
2531 	SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon);
2532 #endif
2533 
2534     ui_get_shellsize();
2535 
2536 #ifdef MCH_WRITE_DUMP
2537     fdDump = fopen("dump", "wt");
2538 
2539     if (fdDump)
2540     {
2541 	time_t t;
2542 
2543 	time(&t);
2544 	fputs(ctime(&t), fdDump);
2545 	fflush(fdDump);
2546     }
2547 #endif
2548 
2549     g_fWindInitCalled = TRUE;
2550 
2551 #ifdef FEAT_MOUSE
2552     g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
2553 #endif
2554 
2555 #ifdef FEAT_CLIPBOARD
2556     win_clip_init();
2557 #endif
2558 
2559     /* This will be NULL on anything but NT 4.0 */
2560     s_pfnGetConsoleKeyboardLayoutName =
2561 	(PFNGCKLN) GetProcAddress(GetModuleHandle("kernel32.dll"),
2562 				  "GetConsoleKeyboardLayoutNameA");
2563 }
2564 
2565 /*
2566  * non-GUI version of mch_exit().
2567  * Shut down and exit with status `r'
2568  * Careful: mch_exit() may be called before mch_init()!
2569  */
2570     void
2571 mch_exit(int r)
2572 {
2573     stoptermcap();
2574 
2575     if (g_fWindInitCalled)
2576 	settmode(TMODE_COOK);
2577 
2578     ml_close_all(TRUE);		/* remove all memfiles */
2579 
2580     if (g_fWindInitCalled)
2581     {
2582 #ifdef FEAT_TITLE
2583 	mch_restore_title(3);
2584 	/*
2585 	 * Restore both the small and big icons of the console window to
2586 	 * what they were at startup.  Don't do this when the window is
2587 	 * closed, Vim would hang here.
2588 	 */
2589 	if (g_fCanChangeIcon && !g_fForceExit)
2590 	    SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon);
2591 #endif
2592 
2593 #ifdef MCH_WRITE_DUMP
2594 	if (fdDump)
2595 	{
2596 	    time_t t;
2597 
2598 	    time(&t);
2599 	    fputs(ctime(&t), fdDump);
2600 	    fclose(fdDump);
2601 	}
2602 	fdDump = NULL;
2603 #endif
2604     }
2605 
2606     SetConsoleCursorInfo(g_hConOut, &g_cci);
2607     SetConsoleMode(g_hConIn,  g_cmodein);
2608     SetConsoleMode(g_hConOut, g_cmodeout);
2609 
2610 #ifdef DYNAMIC_GETTEXT
2611     dyn_libintl_end();
2612 #endif
2613 
2614     exit(r);
2615 }
2616 #endif /* !FEAT_GUI_W32 */
2617 
2618 /*
2619  * Do we have an interactive window?
2620  */
2621 /*ARGSUSED*/
2622     int
2623 mch_check_win(
2624     int argc,
2625     char **argv)
2626 {
2627     get_exe_name();
2628 
2629 #ifdef FEAT_GUI_W32
2630     return OK;	    /* GUI always has a tty */
2631 #else
2632     if (isatty(1))
2633 	return OK;
2634     return FAIL;
2635 #endif
2636 }
2637 
2638 
2639 #ifdef FEAT_MBYTE
2640 /*
2641  * fname_casew(): Wide version of fname_case().  Set the case of the file name,
2642  * if it already exists.  When "len" is > 0, also expand short to long
2643  * filenames.
2644  * Return FAIL if wide functions are not available, OK otherwise.
2645  * NOTE: much of this is identical to fname_case(), keep in sync!
2646  */
2647     static int
2648 fname_casew(
2649     WCHAR	*name,
2650     int		len)
2651 {
2652     WCHAR		szTrueName[_MAX_PATH + 2];
2653     WCHAR		szTrueNameTemp[_MAX_PATH + 2];
2654     WCHAR		*ptrue, *ptruePrev;
2655     WCHAR		*porig, *porigPrev;
2656     int			flen;
2657     WIN32_FIND_DATAW	fb;
2658     HANDLE		hFind = INVALID_HANDLE_VALUE;
2659     int			c;
2660     int			slen;
2661 
2662     flen = (int)wcslen(name);
2663     if (flen > _MAX_PATH)
2664 	return OK;
2665 
2666     /* slash_adjust(name) not needed, already adjusted by fname_case(). */
2667 
2668     /* Build the new name in szTrueName[] one component at a time. */
2669     porig = name;
2670     ptrue = szTrueName;
2671 
2672     if (iswalpha(porig[0]) && porig[1] == L':')
2673     {
2674 	/* copy leading drive letter */
2675 	*ptrue++ = *porig++;
2676 	*ptrue++ = *porig++;
2677     }
2678     *ptrue = NUL;	    /* in case nothing follows */
2679 
2680     while (*porig != NUL)
2681     {
2682 	/* copy \ characters */
2683 	while (*porig == psepc)
2684 	    *ptrue++ = *porig++;
2685 
2686 	ptruePrev = ptrue;
2687 	porigPrev = porig;
2688 	while (*porig != NUL && *porig != psepc)
2689 	{
2690 	    *ptrue++ = *porig++;
2691 	}
2692 	*ptrue = NUL;
2693 
2694 	/* To avoid a slow failure append "\*" when searching a directory,
2695 	 * server or network share. */
2696 	wcscpy(szTrueNameTemp, szTrueName);
2697 	slen = (int)wcslen(szTrueNameTemp);
2698 	if (*porig == psepc && slen + 2 < _MAX_PATH)
2699 	    wcscpy(szTrueNameTemp + slen, L"\\*");
2700 
2701 	/* Skip "", "." and "..". */
2702 	if (ptrue > ptruePrev
2703 		&& (ptruePrev[0] != L'.'
2704 		    || (ptruePrev[1] != NUL
2705 			&& (ptruePrev[1] != L'.' || ptruePrev[2] != NUL)))
2706 		&& (hFind = FindFirstFileW(szTrueNameTemp, &fb))
2707 						      != INVALID_HANDLE_VALUE)
2708 	{
2709 	    c = *porig;
2710 	    *porig = NUL;
2711 
2712 	    /* Only use the match when it's the same name (ignoring case) or
2713 	     * expansion is allowed and there is a match with the short name
2714 	     * and there is enough room. */
2715 	    if (_wcsicoll(porigPrev, fb.cFileName) == 0
2716 		    || (len > 0
2717 			&& (_wcsicoll(porigPrev, fb.cAlternateFileName) == 0
2718 			    && (int)(ptruePrev - szTrueName)
2719 					   + (int)wcslen(fb.cFileName) < len)))
2720 	    {
2721 		wcscpy(ptruePrev, fb.cFileName);
2722 
2723 		/* Look for exact match and prefer it if found.  Must be a
2724 		 * long name, otherwise there would be only one match. */
2725 		while (FindNextFileW(hFind, &fb))
2726 		{
2727 		    if (*fb.cAlternateFileName != NUL
2728 			    && (wcscoll(porigPrev, fb.cFileName) == 0
2729 				|| (len > 0
2730 				    && (_wcsicoll(porigPrev,
2731 						   fb.cAlternateFileName) == 0
2732 				    && (int)(ptruePrev - szTrueName)
2733 					 + (int)wcslen(fb.cFileName) < len))))
2734 		    {
2735 			wcscpy(ptruePrev, fb.cFileName);
2736 			break;
2737 		    }
2738 		}
2739 	    }
2740 	    FindClose(hFind);
2741 	    *porig = c;
2742 	    ptrue = ptruePrev + wcslen(ptruePrev);
2743 	}
2744 	else if (hFind == INVALID_HANDLE_VALUE
2745 		&& GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2746 	    return FAIL;
2747     }
2748 
2749     wcscpy(name, szTrueName);
2750     return OK;
2751 }
2752 #endif
2753 
2754 /*
2755  * fname_case(): Set the case of the file name, if it already exists.
2756  * When "len" is > 0, also expand short to long filenames.
2757  * NOTE: much of this is identical to fname_casew(), keep in sync!
2758  */
2759     void
2760 fname_case(
2761     char_u	*name,
2762     int		len)
2763 {
2764     char		szTrueName[_MAX_PATH + 2];
2765     char		szTrueNameTemp[_MAX_PATH + 2];
2766     char		*ptrue, *ptruePrev;
2767     char		*porig, *porigPrev;
2768     int			flen;
2769     WIN32_FIND_DATA	fb;
2770     HANDLE		hFind;
2771     int			c;
2772     int			slen;
2773 
2774     flen = (int)STRLEN(name);
2775     if (flen == 0)
2776 	return;
2777 
2778     slash_adjust(name);
2779 
2780 #ifdef FEAT_MBYTE
2781     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2782     {
2783 	WCHAR	*p = enc_to_utf16(name, NULL);
2784 
2785 	if (p != NULL)
2786 	{
2787 	    char_u	*q;
2788 	    WCHAR	buf[_MAX_PATH + 1];
2789 
2790 	    wcsncpy(buf, p, _MAX_PATH);
2791 	    buf[_MAX_PATH] = L'\0';
2792 	    vim_free(p);
2793 
2794 	    if (fname_casew(buf, (len > 0) ? _MAX_PATH : 0) == OK)
2795 	    {
2796 		q = utf16_to_enc(buf, NULL);
2797 		if (q != NULL)
2798 		{
2799 		    vim_strncpy(name, q, (len > 0) ? len - 1 : flen);
2800 		    vim_free(q);
2801 		    return;
2802 		}
2803 	    }
2804 	}
2805 	/* Retry with non-wide function (for Windows 98). */
2806     }
2807 #endif
2808 
2809     /* If 'enc' is utf-8, flen can be larger than _MAX_PATH.
2810      * So we should check this after calling wide function. */
2811     if (flen > _MAX_PATH)
2812 	return;
2813 
2814     /* Build the new name in szTrueName[] one component at a time. */
2815     porig = name;
2816     ptrue = szTrueName;
2817 
2818     if (isalpha(porig[0]) && porig[1] == ':')
2819     {
2820 	/* copy leading drive letter */
2821 	*ptrue++ = *porig++;
2822 	*ptrue++ = *porig++;
2823     }
2824     *ptrue = NUL;	    /* in case nothing follows */
2825 
2826     while (*porig != NUL)
2827     {
2828 	/* copy \ characters */
2829 	while (*porig == psepc)
2830 	    *ptrue++ = *porig++;
2831 
2832 	ptruePrev = ptrue;
2833 	porigPrev = porig;
2834 	while (*porig != NUL && *porig != psepc)
2835 	{
2836 #ifdef FEAT_MBYTE
2837 	    int l;
2838 
2839 	    if (enc_dbcs)
2840 	    {
2841 		l = (*mb_ptr2len)(porig);
2842 		while (--l >= 0)
2843 		    *ptrue++ = *porig++;
2844 	    }
2845 	    else
2846 #endif
2847 		*ptrue++ = *porig++;
2848 	}
2849 	*ptrue = NUL;
2850 
2851 	/* To avoid a slow failure append "\*" when searching a directory,
2852 	 * server or network share. */
2853 	STRCPY(szTrueNameTemp, szTrueName);
2854 	slen = (int)strlen(szTrueNameTemp);
2855 	if (*porig == psepc && slen + 2 < _MAX_PATH)
2856 	    STRCPY(szTrueNameTemp + slen, "\\*");
2857 
2858 	/* Skip "", "." and "..". */
2859 	if (ptrue > ptruePrev
2860 		&& (ptruePrev[0] != '.'
2861 		    || (ptruePrev[1] != NUL
2862 			&& (ptruePrev[1] != '.' || ptruePrev[2] != NUL)))
2863 		&& (hFind = FindFirstFile(szTrueNameTemp, &fb))
2864 						      != INVALID_HANDLE_VALUE)
2865 	{
2866 	    c = *porig;
2867 	    *porig = NUL;
2868 
2869 	    /* Only use the match when it's the same name (ignoring case) or
2870 	     * expansion is allowed and there is a match with the short name
2871 	     * and there is enough room. */
2872 	    if (_stricoll(porigPrev, fb.cFileName) == 0
2873 		    || (len > 0
2874 			&& (_stricoll(porigPrev, fb.cAlternateFileName) == 0
2875 			    && (int)(ptruePrev - szTrueName)
2876 					   + (int)strlen(fb.cFileName) < len)))
2877 	    {
2878 		STRCPY(ptruePrev, fb.cFileName);
2879 
2880 		/* Look for exact match and prefer it if found.  Must be a
2881 		 * long name, otherwise there would be only one match. */
2882 		while (FindNextFile(hFind, &fb))
2883 		{
2884 		    if (*fb.cAlternateFileName != NUL
2885 			    && (strcoll(porigPrev, fb.cFileName) == 0
2886 				|| (len > 0
2887 				    && (_stricoll(porigPrev,
2888 						   fb.cAlternateFileName) == 0
2889 				    && (int)(ptruePrev - szTrueName)
2890 					 + (int)strlen(fb.cFileName) < len))))
2891 		    {
2892 			STRCPY(ptruePrev, fb.cFileName);
2893 			break;
2894 		    }
2895 		}
2896 	    }
2897 	    FindClose(hFind);
2898 	    *porig = c;
2899 	    ptrue = ptruePrev + strlen(ptruePrev);
2900 	}
2901     }
2902 
2903     STRCPY(name, szTrueName);
2904 }
2905 
2906 
2907 /*
2908  * Insert user name in s[len].
2909  */
2910     int
2911 mch_get_user_name(
2912     char_u  *s,
2913     int	    len)
2914 {
2915     char szUserName[256 + 1];	/* UNLEN is 256 */
2916     DWORD cch = sizeof szUserName;
2917 
2918 #ifdef FEAT_MBYTE
2919     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2920     {
2921 	WCHAR wszUserName[256 + 1];	/* UNLEN is 256 */
2922 	DWORD wcch = sizeof(wszUserName) / sizeof(WCHAR);
2923 
2924 	if (GetUserNameW(wszUserName, &wcch))
2925 	{
2926 	    char_u  *p = utf16_to_enc(wszUserName, NULL);
2927 
2928 	    if (p != NULL)
2929 	    {
2930 		vim_strncpy(s, p, len - 1);
2931 		vim_free(p);
2932 		return OK;
2933 	    }
2934 	}
2935 	else if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
2936 	    return FAIL;
2937 	/* Retry with non-wide function (for Windows 98). */
2938     }
2939 #endif
2940     if (GetUserName(szUserName, &cch))
2941     {
2942 	vim_strncpy(s, szUserName, len - 1);
2943 	return OK;
2944     }
2945     s[0] = NUL;
2946     return FAIL;
2947 }
2948 
2949 
2950 /*
2951  * Insert host name in s[len].
2952  */
2953     void
2954 mch_get_host_name(
2955     char_u	*s,
2956     int		len)
2957 {
2958     DWORD cch = len;
2959 
2960 #ifdef FEAT_MBYTE
2961     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2962     {
2963 	WCHAR wszHostName[256 + 1];
2964 	DWORD wcch = sizeof(wszHostName) / sizeof(WCHAR);
2965 
2966 	if (GetComputerNameW(wszHostName, &wcch))
2967 	{
2968 	    char_u  *p = utf16_to_enc(wszHostName, NULL);
2969 
2970 	    if (p != NULL)
2971 	    {
2972 		vim_strncpy(s, p, len - 1);
2973 		vim_free(p);
2974 		return;
2975 	    }
2976 	}
2977 	else if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
2978 	    return;
2979 	/* Retry with non-wide function (for Windows 98). */
2980     }
2981 #endif
2982     if (!GetComputerName(s, &cch))
2983 	vim_strncpy(s, "PC (Win32 Vim)", len - 1);
2984 }
2985 
2986 
2987 /*
2988  * return process ID
2989  */
2990     long
2991 mch_get_pid(void)
2992 {
2993     return (long)GetCurrentProcessId();
2994 }
2995 
2996 
2997 /*
2998  * Get name of current directory into buffer 'buf' of length 'len' bytes.
2999  * Return OK for success, FAIL for failure.
3000  */
3001     int
3002 mch_dirname(
3003     char_u	*buf,
3004     int		len)
3005 {
3006     /*
3007      * Originally this was:
3008      *    return (getcwd(buf, len) != NULL ? OK : FAIL);
3009      * But the Win32s known bug list says that getcwd() doesn't work
3010      * so use the Win32 system call instead. <Negri>
3011      */
3012 #ifdef FEAT_MBYTE
3013     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3014     {
3015 	WCHAR	wbuf[_MAX_PATH + 1];
3016 
3017 	if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0)
3018 	{
3019 	    char_u  *p = utf16_to_enc(wbuf, NULL);
3020 
3021 	    if (p != NULL)
3022 	    {
3023 		vim_strncpy(buf, p, len - 1);
3024 		vim_free(p);
3025 		return OK;
3026 	    }
3027 	}
3028 	else if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3029 	    return FAIL;
3030 	/* Retry with non-wide function (for Windows 98). */
3031     }
3032 #endif
3033     return (GetCurrentDirectory(len, buf) != 0 ? OK : FAIL);
3034 }
3035 
3036 /*
3037  * Get file permissions for "name".
3038  * Return mode_t or -1 for error.
3039  */
3040     long
3041 mch_getperm(char_u *name)
3042 {
3043     struct stat st;
3044     int		n;
3045 
3046     n = mch_stat(name, &st);
3047     return n == 0 ? (long)(unsigned short)st.st_mode : -1L;
3048 }
3049 
3050 
3051 /*
3052  * Set file permission for "name" to "perm".
3053  *
3054  * Return FAIL for failure, OK otherwise.
3055  */
3056     int
3057 mch_setperm(char_u *name, long perm)
3058 {
3059     long	n = -1;
3060 
3061 #ifdef FEAT_MBYTE
3062     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3063     {
3064 	WCHAR *p = enc_to_utf16(name, NULL);
3065 
3066 	if (p != NULL)
3067 	{
3068 	    n = _wchmod(p, perm);
3069 	    vim_free(p);
3070 	    if (n == -1 && g_PlatformId == VER_PLATFORM_WIN32_NT)
3071 		return FAIL;
3072 	    /* Retry with non-wide function (for Windows 98). */
3073 	}
3074     }
3075     if (n == -1)
3076 #endif
3077 	n = _chmod(name, perm);
3078     if (n == -1)
3079 	return FAIL;
3080 
3081     win32_set_archive(name);
3082 
3083     return OK;
3084 }
3085 
3086 /*
3087  * Set hidden flag for "name".
3088  */
3089     void
3090 mch_hide(char_u *name)
3091 {
3092     int attrs = win32_getattrs(name);
3093     if (attrs == -1)
3094 	return;
3095 
3096     attrs |= FILE_ATTRIBUTE_HIDDEN;
3097     win32_setattrs(name, attrs);
3098 }
3099 
3100 /*
3101  * Return TRUE if file "name" exists and is hidden.
3102  */
3103     int
3104 mch_ishidden(char_u *name)
3105 {
3106     int f = win32_getattrs(name);
3107 
3108     if (f == -1)
3109 	return FALSE;		    /* file does not exist at all */
3110 
3111     return (f & FILE_ATTRIBUTE_HIDDEN) != 0;
3112 }
3113 
3114 /*
3115  * return TRUE if "name" is a directory
3116  * return FALSE if "name" is not a directory or upon error
3117  */
3118     int
3119 mch_isdir(char_u *name)
3120 {
3121     int f = win32_getattrs(name);
3122 
3123     if (f == -1)
3124 	return FALSE;		    /* file does not exist at all */
3125 
3126     return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
3127 }
3128 
3129 /*
3130  * Create directory "name".
3131  * Return 0 on success, -1 on error.
3132  */
3133     int
3134 mch_mkdir(char_u *name)
3135 {
3136 #ifdef FEAT_MBYTE
3137     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3138     {
3139 	WCHAR	*p;
3140 	int	retval;
3141 
3142 	p = enc_to_utf16(name, NULL);
3143 	if (p == NULL)
3144 	    return -1;
3145 	retval = _wmkdir(p);
3146 	vim_free(p);
3147 	return retval;
3148     }
3149 #endif
3150     return _mkdir(name);
3151 }
3152 
3153 /*
3154  * Return TRUE if file "fname" has more than one link.
3155  */
3156     int
3157 mch_is_hard_link(char_u *fname)
3158 {
3159     BY_HANDLE_FILE_INFORMATION info;
3160 
3161     return win32_fileinfo(fname, &info) == FILEINFO_OK
3162 						   && info.nNumberOfLinks > 1;
3163 }
3164 
3165 /*
3166  * Return TRUE if file "fname" is a symbolic link.
3167  */
3168     int
3169 mch_is_symbolic_link(char_u *fname)
3170 {
3171     HANDLE		hFind;
3172     int			res = FALSE;
3173     WIN32_FIND_DATAA	findDataA;
3174     DWORD		fileFlags = 0, reparseTag = 0;
3175 #ifdef FEAT_MBYTE
3176     WCHAR		*wn = NULL;
3177     WIN32_FIND_DATAW	findDataW;
3178 
3179     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3180 	wn = enc_to_utf16(fname, NULL);
3181     if (wn != NULL)
3182     {
3183 	hFind = FindFirstFileW(wn, &findDataW);
3184 	vim_free(wn);
3185 	if (hFind == INVALID_HANDLE_VALUE
3186 		&& GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3187 	{
3188 	    /* Retry with non-wide function (for Windows 98). */
3189 	    hFind = FindFirstFile(fname, &findDataA);
3190 	    if (hFind != INVALID_HANDLE_VALUE)
3191 	    {
3192 		fileFlags = findDataA.dwFileAttributes;
3193 		reparseTag = findDataA.dwReserved0;
3194 	    }
3195 	}
3196 	else
3197 	{
3198 	    fileFlags = findDataW.dwFileAttributes;
3199 	    reparseTag = findDataW.dwReserved0;
3200 	}
3201     }
3202     else
3203 #endif
3204     {
3205 	hFind = FindFirstFile(fname, &findDataA);
3206 	if (hFind != INVALID_HANDLE_VALUE)
3207 	{
3208 	    fileFlags = findDataA.dwFileAttributes;
3209 	    reparseTag = findDataA.dwReserved0;
3210 	}
3211     }
3212 
3213     if (hFind != INVALID_HANDLE_VALUE)
3214 	FindClose(hFind);
3215 
3216     if ((fileFlags & FILE_ATTRIBUTE_REPARSE_POINT)
3217 	    && reparseTag == IO_REPARSE_TAG_SYMLINK)
3218 	res = TRUE;
3219 
3220     return res;
3221 }
3222 
3223 /*
3224  * Return TRUE if file "fname" has more than one link or if it is a symbolic
3225  * link.
3226  */
3227     int
3228 mch_is_linked(char_u *fname)
3229 {
3230     if (mch_is_hard_link(fname) || mch_is_symbolic_link(fname))
3231 	return TRUE;
3232     return FALSE;
3233 }
3234 
3235 /*
3236  * Get the by-handle-file-information for "fname".
3237  * Returns FILEINFO_OK when OK.
3238  * returns FILEINFO_ENC_FAIL when enc_to_utf16() failed.
3239  * Returns FILEINFO_READ_FAIL when CreateFile() failed.
3240  * Returns FILEINFO_INFO_FAIL when GetFileInformationByHandle() failed.
3241  */
3242     int
3243 win32_fileinfo(char_u *fname, BY_HANDLE_FILE_INFORMATION *info)
3244 {
3245     HANDLE	hFile;
3246     int		res = FILEINFO_READ_FAIL;
3247 #ifdef FEAT_MBYTE
3248     WCHAR	*wn = NULL;
3249 
3250     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3251     {
3252 	wn = enc_to_utf16(fname, NULL);
3253 	if (wn == NULL)
3254 	    res = FILEINFO_ENC_FAIL;
3255     }
3256     if (wn != NULL)
3257     {
3258 	hFile = CreateFileW(wn,		/* file name */
3259 		    GENERIC_READ,	/* access mode */
3260 		    FILE_SHARE_READ | FILE_SHARE_WRITE,	/* share mode */
3261 		    NULL,		/* security descriptor */
3262 		    OPEN_EXISTING,	/* creation disposition */
3263 		    FILE_FLAG_BACKUP_SEMANTICS,	/* file attributes */
3264 		    NULL);		/* handle to template file */
3265 	if (hFile == INVALID_HANDLE_VALUE
3266 			      && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3267 	{
3268 	    /* Retry with non-wide function (for Windows 98). */
3269 	    vim_free(wn);
3270 	    wn = NULL;
3271 	}
3272     }
3273     if (wn == NULL)
3274 #endif
3275 	hFile = CreateFile(fname,	/* file name */
3276 		    GENERIC_READ,	/* access mode */
3277 		    FILE_SHARE_READ | FILE_SHARE_WRITE,	/* share mode */
3278 		    NULL,		/* security descriptor */
3279 		    OPEN_EXISTING,	/* creation disposition */
3280 		    FILE_FLAG_BACKUP_SEMANTICS,	/* file attributes */
3281 		    NULL);		/* handle to template file */
3282 
3283     if (hFile != INVALID_HANDLE_VALUE)
3284     {
3285 	if (GetFileInformationByHandle(hFile, info) != 0)
3286 	    res = FILEINFO_OK;
3287 	else
3288 	    res = FILEINFO_INFO_FAIL;
3289 	CloseHandle(hFile);
3290     }
3291 
3292 #ifdef FEAT_MBYTE
3293     vim_free(wn);
3294 #endif
3295     return res;
3296 }
3297 
3298 /*
3299  * get file attributes for `name'
3300  * -1 : error
3301  * else FILE_ATTRIBUTE_* defined in winnt.h
3302  */
3303     static int
3304 win32_getattrs(char_u *name)
3305 {
3306     int		attr;
3307 #ifdef FEAT_MBYTE
3308     WCHAR	*p = NULL;
3309 
3310     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3311 	p = enc_to_utf16(name, NULL);
3312 
3313     if (p != NULL)
3314     {
3315 	attr = GetFileAttributesW(p);
3316 	if (attr < 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3317 	{
3318 	    /* Retry with non-wide function (for Windows 98). */
3319 	    vim_free(p);
3320 	    p = NULL;
3321 	}
3322     }
3323     if (p == NULL)
3324 #endif
3325 	attr = GetFileAttributes((char *)name);
3326 #ifdef FEAT_MBYTE
3327     vim_free(p);
3328 #endif
3329     return attr;
3330 }
3331 
3332 /*
3333  * set file attributes for `name' to `attrs'
3334  *
3335  * return -1 for failure, 0 otherwise
3336  */
3337     static
3338     int
3339 win32_setattrs(char_u *name, int attrs)
3340 {
3341     int res;
3342 #ifdef FEAT_MBYTE
3343     WCHAR	*p = NULL;
3344 
3345     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3346 	p = enc_to_utf16(name, NULL);
3347 
3348     if (p != NULL)
3349     {
3350 	res = SetFileAttributesW(p, attrs);
3351 	if (res == FALSE
3352 	    && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3353 	{
3354 	    /* Retry with non-wide function (for Windows 98). */
3355 	    vim_free(p);
3356 	    p = NULL;
3357 	}
3358     }
3359     if (p == NULL)
3360 #endif
3361 	res = SetFileAttributes((char *)name, attrs);
3362 #ifdef FEAT_MBYTE
3363     vim_free(p);
3364 #endif
3365     return res ? 0 : -1;
3366 }
3367 
3368 /*
3369  * Set archive flag for "name".
3370  */
3371     static
3372     int
3373 win32_set_archive(char_u *name)
3374 {
3375     int attrs = win32_getattrs(name);
3376     if (attrs == -1)
3377 	return -1;
3378 
3379     attrs |= FILE_ATTRIBUTE_ARCHIVE;
3380     return win32_setattrs(name, attrs);
3381 }
3382 
3383 /*
3384  * Return TRUE if file or directory "name" is writable (not readonly).
3385  * Strange semantics of Win32: a readonly directory is writable, but you can't
3386  * delete a file.  Let's say this means it is writable.
3387  */
3388     int
3389 mch_writable(char_u *name)
3390 {
3391     int attrs = win32_getattrs(name);
3392 
3393     return (attrs != -1 && (!(attrs & FILE_ATTRIBUTE_READONLY)
3394 			  || (attrs & FILE_ATTRIBUTE_DIRECTORY)));
3395 }
3396 
3397 /*
3398  * Return 1 if "name" can be executed, 0 if not.
3399  * If "use_path" is FALSE only check if "name" is executable.
3400  * Return -1 if unknown.
3401  */
3402     int
3403 mch_can_exe(char_u *name, char_u **path, int use_path)
3404 {
3405     char_u	buf[_MAX_PATH];
3406     int		len = (int)STRLEN(name);
3407     char_u	*p;
3408 
3409     if (len >= _MAX_PATH)	/* safety check */
3410 	return FALSE;
3411     if (!use_path)
3412     {
3413 	/* TODO: check if file is really executable. */
3414 	return mch_getperm(name) != -1 && !mch_isdir(name);
3415     }
3416 
3417     /* If there already is an extension try using the name directly.  Also do
3418      * this with a Unix-shell like 'shell'. */
3419     if (vim_strchr(gettail(name), '.') != NULL
3420 			       || strstr((char *)gettail(p_sh), "sh") != NULL)
3421 	if (executable_exists((char *)name, path))
3422 	    return TRUE;
3423 
3424     /*
3425      * Loop over all extensions in $PATHEXT.
3426      */
3427     vim_strncpy(buf, name, _MAX_PATH - 1);
3428     p = mch_getenv("PATHEXT");
3429     if (p == NULL)
3430 	p = (char_u *)".com;.exe;.bat;.cmd";
3431     while (*p)
3432     {
3433 	if (p[0] == '.' && (p[1] == NUL || p[1] == ';'))
3434 	{
3435 	    /* A single "." means no extension is added. */
3436 	    buf[len] = NUL;
3437 	    ++p;
3438 	    if (*p)
3439 		++p;
3440 	}
3441 	else
3442 	    copy_option_part(&p, buf + len, _MAX_PATH - len, ";");
3443 	if (executable_exists((char *)buf, path))
3444 	    return TRUE;
3445     }
3446     return FALSE;
3447 }
3448 
3449 /*
3450  * Check what "name" is:
3451  * NODE_NORMAL: file or directory (or doesn't exist)
3452  * NODE_WRITABLE: writable device, socket, fifo, etc.
3453  * NODE_OTHER: non-writable things
3454  */
3455     int
3456 mch_nodetype(char_u *name)
3457 {
3458     HANDLE	hFile;
3459     int		type;
3460 #ifdef FEAT_MBYTE
3461     WCHAR	*wn = NULL;
3462 #endif
3463 
3464     /* We can't open a file with a name "\\.\con" or "\\.\prn" and trying to
3465      * read from it later will cause Vim to hang.  Thus return NODE_WRITABLE
3466      * here. */
3467     if (STRNCMP(name, "\\\\.\\", 4) == 0)
3468 	return NODE_WRITABLE;
3469 
3470 #ifdef FEAT_MBYTE
3471     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3472     {
3473 	wn = enc_to_utf16(name, NULL);
3474 	if (wn != NULL)
3475 	{
3476 	    hFile = CreateFileW(wn,	/* file name */
3477 			GENERIC_WRITE,	/* access mode */
3478 			0,		/* share mode */
3479 			NULL,		/* security descriptor */
3480 			OPEN_EXISTING,	/* creation disposition */
3481 			0,		/* file attributes */
3482 			NULL);		/* handle to template file */
3483 	    if (hFile == INVALID_HANDLE_VALUE
3484 			      && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3485 	    {
3486 		/* Retry with non-wide function (for Windows 98). */
3487 		vim_free(wn);
3488 		wn = NULL;
3489 	    }
3490 	}
3491     }
3492     if (wn == NULL)
3493 #endif
3494 	hFile = CreateFile(name,	/* file name */
3495 		    GENERIC_WRITE,	/* access mode */
3496 		    0,			/* share mode */
3497 		    NULL,		/* security descriptor */
3498 		    OPEN_EXISTING,	/* creation disposition */
3499 		    0,			/* file attributes */
3500 		    NULL);		/* handle to template file */
3501 
3502 #ifdef FEAT_MBYTE
3503     vim_free(wn);
3504 #endif
3505     if (hFile == INVALID_HANDLE_VALUE)
3506 	return NODE_NORMAL;
3507 
3508     type = GetFileType(hFile);
3509     CloseHandle(hFile);
3510     if (type == FILE_TYPE_CHAR)
3511 	return NODE_WRITABLE;
3512     if (type == FILE_TYPE_DISK)
3513 	return NODE_NORMAL;
3514     return NODE_OTHER;
3515 }
3516 
3517 #ifdef HAVE_ACL
3518 struct my_acl
3519 {
3520     PSECURITY_DESCRIPTOR    pSecurityDescriptor;
3521     PSID		    pSidOwner;
3522     PSID		    pSidGroup;
3523     PACL		    pDacl;
3524     PACL		    pSacl;
3525 };
3526 #endif
3527 
3528 /*
3529  * Return a pointer to the ACL of file "fname" in allocated memory.
3530  * Return NULL if the ACL is not available for whatever reason.
3531  */
3532     vim_acl_T
3533 mch_get_acl(char_u *fname)
3534 {
3535 #ifndef HAVE_ACL
3536     return (vim_acl_T)NULL;
3537 #else
3538     struct my_acl   *p = NULL;
3539     DWORD   err;
3540 
3541     /* This only works on Windows NT and 2000. */
3542     if (g_PlatformId == VER_PLATFORM_WIN32_NT && advapi_lib != NULL)
3543     {
3544 	p = (struct my_acl *)alloc_clear((unsigned)sizeof(struct my_acl));
3545 	if (p != NULL)
3546 	{
3547 # ifdef FEAT_MBYTE
3548 	    WCHAR	*wn = NULL;
3549 
3550 	    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3551 		wn = enc_to_utf16(fname, NULL);
3552 	    if (wn != NULL)
3553 	    {
3554 		/* Try to retrieve the entire security descriptor. */
3555 		err = pGetNamedSecurityInfoW(
3556 			    wn,			// Abstract filename
3557 			    SE_FILE_OBJECT,	// File Object
3558 			    OWNER_SECURITY_INFORMATION |
3559 			    GROUP_SECURITY_INFORMATION |
3560 			    DACL_SECURITY_INFORMATION |
3561 			    SACL_SECURITY_INFORMATION,
3562 			    &p->pSidOwner,	// Ownership information.
3563 			    &p->pSidGroup,	// Group membership.
3564 			    &p->pDacl,		// Discretionary information.
3565 			    &p->pSacl,		// For auditing purposes.
3566 			    &p->pSecurityDescriptor);
3567 		if (err == ERROR_ACCESS_DENIED ||
3568 			err == ERROR_PRIVILEGE_NOT_HELD)
3569 		{
3570 		    /* Retrieve only DACL. */
3571 		    (void)pGetNamedSecurityInfoW(
3572 			    wn,
3573 			    SE_FILE_OBJECT,
3574 			    DACL_SECURITY_INFORMATION,
3575 			    NULL,
3576 			    NULL,
3577 			    &p->pDacl,
3578 			    NULL,
3579 			    &p->pSecurityDescriptor);
3580 		}
3581 		if (p->pSecurityDescriptor == NULL)
3582 		{
3583 		    mch_free_acl((vim_acl_T)p);
3584 		    p = NULL;
3585 		}
3586 		vim_free(wn);
3587 	    }
3588 	    else
3589 # endif
3590 	    {
3591 		/* Try to retrieve the entire security descriptor. */
3592 		err = pGetNamedSecurityInfo(
3593 			    (LPSTR)fname,	// Abstract filename
3594 			    SE_FILE_OBJECT,	// File Object
3595 			    OWNER_SECURITY_INFORMATION |
3596 			    GROUP_SECURITY_INFORMATION |
3597 			    DACL_SECURITY_INFORMATION |
3598 			    SACL_SECURITY_INFORMATION,
3599 			    &p->pSidOwner,	// Ownership information.
3600 			    &p->pSidGroup,	// Group membership.
3601 			    &p->pDacl,		// Discretionary information.
3602 			    &p->pSacl,		// For auditing purposes.
3603 			    &p->pSecurityDescriptor);
3604 		if (err == ERROR_ACCESS_DENIED ||
3605 			err == ERROR_PRIVILEGE_NOT_HELD)
3606 		{
3607 		    /* Retrieve only DACL. */
3608 		    (void)pGetNamedSecurityInfo(
3609 			    (LPSTR)fname,
3610 			    SE_FILE_OBJECT,
3611 			    DACL_SECURITY_INFORMATION,
3612 			    NULL,
3613 			    NULL,
3614 			    &p->pDacl,
3615 			    NULL,
3616 			    &p->pSecurityDescriptor);
3617 		}
3618 		if (p->pSecurityDescriptor == NULL)
3619 		{
3620 		    mch_free_acl((vim_acl_T)p);
3621 		    p = NULL;
3622 		}
3623 	    }
3624 	}
3625     }
3626 
3627     return (vim_acl_T)p;
3628 #endif
3629 }
3630 
3631 #ifdef HAVE_ACL
3632 /*
3633  * Check if "acl" contains inherited ACE.
3634  */
3635     static BOOL
3636 is_acl_inherited(PACL acl)
3637 {
3638     DWORD   i;
3639     ACL_SIZE_INFORMATION    acl_info;
3640     PACCESS_ALLOWED_ACE	    ace;
3641 
3642     acl_info.AceCount = 0;
3643     GetAclInformation(acl, &acl_info, sizeof(acl_info), AclSizeInformation);
3644     for (i = 0; i < acl_info.AceCount; i++)
3645     {
3646 	GetAce(acl, i, (LPVOID *)&ace);
3647 	if (ace->Header.AceFlags & INHERITED_ACE)
3648 	    return TRUE;
3649     }
3650     return FALSE;
3651 }
3652 #endif
3653 
3654 /*
3655  * Set the ACL of file "fname" to "acl" (unless it's NULL).
3656  * Errors are ignored.
3657  * This must only be called with "acl" equal to what mch_get_acl() returned.
3658  */
3659     void
3660 mch_set_acl(char_u *fname, vim_acl_T acl)
3661 {
3662 #ifdef HAVE_ACL
3663     struct my_acl   *p = (struct my_acl *)acl;
3664     SECURITY_INFORMATION    sec_info = 0;
3665 
3666     if (p != NULL && advapi_lib != NULL)
3667     {
3668 # ifdef FEAT_MBYTE
3669 	WCHAR	*wn = NULL;
3670 # endif
3671 
3672 	/* Set security flags */
3673 	if (p->pSidOwner)
3674 	    sec_info |= OWNER_SECURITY_INFORMATION;
3675 	if (p->pSidGroup)
3676 	    sec_info |= GROUP_SECURITY_INFORMATION;
3677 	if (p->pDacl)
3678 	{
3679 	    sec_info |= DACL_SECURITY_INFORMATION;
3680 	    /* Do not inherit its parent's DACL.
3681 	     * If the DACL is inherited, Cygwin permissions would be changed.
3682 	     */
3683 	    if (!is_acl_inherited(p->pDacl))
3684 		sec_info |= PROTECTED_DACL_SECURITY_INFORMATION;
3685 	}
3686 	if (p->pSacl)
3687 	    sec_info |= SACL_SECURITY_INFORMATION;
3688 
3689 # ifdef FEAT_MBYTE
3690 	if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3691 	    wn = enc_to_utf16(fname, NULL);
3692 	if (wn != NULL)
3693 	{
3694 	    (void)pSetNamedSecurityInfoW(
3695 			wn,			// Abstract filename
3696 			SE_FILE_OBJECT,		// File Object
3697 			sec_info,
3698 			p->pSidOwner,		// Ownership information.
3699 			p->pSidGroup,		// Group membership.
3700 			p->pDacl,		// Discretionary information.
3701 			p->pSacl		// For auditing purposes.
3702 			);
3703 	    vim_free(wn);
3704 	}
3705 	else
3706 # endif
3707 	{
3708 	    (void)pSetNamedSecurityInfo(
3709 			(LPSTR)fname,		// Abstract filename
3710 			SE_FILE_OBJECT,		// File Object
3711 			sec_info,
3712 			p->pSidOwner,		// Ownership information.
3713 			p->pSidGroup,		// Group membership.
3714 			p->pDacl,		// Discretionary information.
3715 			p->pSacl		// For auditing purposes.
3716 			);
3717 	}
3718     }
3719 #endif
3720 }
3721 
3722     void
3723 mch_free_acl(vim_acl_T acl)
3724 {
3725 #ifdef HAVE_ACL
3726     struct my_acl   *p = (struct my_acl *)acl;
3727 
3728     if (p != NULL)
3729     {
3730 	LocalFree(p->pSecurityDescriptor);	// Free the memory just in case
3731 	vim_free(p);
3732     }
3733 #endif
3734 }
3735 
3736 #ifndef FEAT_GUI_W32
3737 
3738 /*
3739  * handler for ctrl-break, ctrl-c interrupts, and fatal events.
3740  */
3741     static BOOL WINAPI
3742 handler_routine(
3743     DWORD dwCtrlType)
3744 {
3745     switch (dwCtrlType)
3746     {
3747     case CTRL_C_EVENT:
3748 	if (ctrl_c_interrupts)
3749 	    g_fCtrlCPressed = TRUE;
3750 	return TRUE;
3751 
3752     case CTRL_BREAK_EVENT:
3753 	g_fCBrkPressed	= TRUE;
3754 	return TRUE;
3755 
3756     /* fatal events: shut down gracefully */
3757     case CTRL_CLOSE_EVENT:
3758     case CTRL_LOGOFF_EVENT:
3759     case CTRL_SHUTDOWN_EVENT:
3760 	windgoto((int)Rows - 1, 0);
3761 	g_fForceExit = TRUE;
3762 
3763 	vim_snprintf((char *)IObuff, IOSIZE, _("Vim: Caught %s event\n"),
3764 		(dwCtrlType == CTRL_CLOSE_EVENT
3765 		     ? _("close")
3766 		     : dwCtrlType == CTRL_LOGOFF_EVENT
3767 			 ? _("logoff")
3768 			 : _("shutdown")));
3769 #ifdef DEBUG
3770 	OutputDebugString(IObuff);
3771 #endif
3772 
3773 	preserve_exit();	/* output IObuff, preserve files and exit */
3774 
3775 	return TRUE;		/* not reached */
3776 
3777     default:
3778 	return FALSE;
3779     }
3780 }
3781 
3782 
3783 /*
3784  * set the tty in (raw) ? "raw" : "cooked" mode
3785  */
3786     void
3787 mch_settmode(int tmode)
3788 {
3789     DWORD cmodein;
3790     DWORD cmodeout;
3791     BOOL bEnableHandler;
3792 
3793     GetConsoleMode(g_hConIn, &cmodein);
3794     GetConsoleMode(g_hConOut, &cmodeout);
3795     if (tmode == TMODE_RAW)
3796     {
3797 	cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3798 		     ENABLE_ECHO_INPUT);
3799 #ifdef FEAT_MOUSE
3800 	if (g_fMouseActive)
3801 	    cmodein |= ENABLE_MOUSE_INPUT;
3802 #endif
3803 	cmodeout &= ~(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3804 	bEnableHandler = TRUE;
3805     }
3806     else /* cooked */
3807     {
3808 	cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3809 		    ENABLE_ECHO_INPUT);
3810 	cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3811 	bEnableHandler = FALSE;
3812     }
3813     SetConsoleMode(g_hConIn, cmodein);
3814     SetConsoleMode(g_hConOut, cmodeout);
3815     SetConsoleCtrlHandler(handler_routine, bEnableHandler);
3816 
3817 #ifdef MCH_WRITE_DUMP
3818     if (fdDump)
3819     {
3820 	fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n",
3821 		tmode == TMODE_RAW ? "raw" :
3822 				    tmode == TMODE_COOK ? "cooked" : "normal",
3823 		cmodein, cmodeout);
3824 	fflush(fdDump);
3825     }
3826 #endif
3827 }
3828 
3829 
3830 /*
3831  * Get the size of the current window in `Rows' and `Columns'
3832  * Return OK when size could be determined, FAIL otherwise.
3833  */
3834     int
3835 mch_get_shellsize(void)
3836 {
3837     CONSOLE_SCREEN_BUFFER_INFO csbi;
3838 
3839     if (!g_fTermcapMode && g_cbTermcap.IsValid)
3840     {
3841 	/*
3842 	 * For some reason, we are trying to get the screen dimensions
3843 	 * even though we are not in termcap mode.  The 'Rows' and 'Columns'
3844 	 * variables are really intended to mean the size of Vim screen
3845 	 * while in termcap mode.
3846 	 */
3847 	Rows = g_cbTermcap.Info.dwSize.Y;
3848 	Columns = g_cbTermcap.Info.dwSize.X;
3849     }
3850     else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3851     {
3852 	Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3853 	Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3854     }
3855     else
3856     {
3857 	Rows = 25;
3858 	Columns = 80;
3859     }
3860     return OK;
3861 }
3862 
3863 /*
3864  * Set a console window to `xSize' * `ySize'
3865  */
3866     static void
3867 ResizeConBufAndWindow(
3868     HANDLE  hConsole,
3869     int	    xSize,
3870     int	    ySize)
3871 {
3872     CONSOLE_SCREEN_BUFFER_INFO csbi;	/* hold current console buffer info */
3873     SMALL_RECT	    srWindowRect;	/* hold the new console size */
3874     COORD	    coordScreen;
3875 
3876 #ifdef MCH_WRITE_DUMP
3877     if (fdDump)
3878     {
3879 	fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
3880 	fflush(fdDump);
3881     }
3882 #endif
3883 
3884     /* get the largest size we can size the console window to */
3885     coordScreen = GetLargestConsoleWindowSize(hConsole);
3886 
3887     /* define the new console window size and scroll position */
3888     srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
3889     srWindowRect.Right =  (SHORT) (min(xSize, coordScreen.X) - 1);
3890     srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
3891 
3892     if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3893     {
3894 	int sx, sy;
3895 
3896 	sx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3897 	sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3898 	if (sy < ySize || sx < xSize)
3899 	{
3900 	    /*
3901 	     * Increasing number of lines/columns, do buffer first.
3902 	     * Use the maximal size in x and y direction.
3903 	     */
3904 	    if (sy < ySize)
3905 		coordScreen.Y = ySize;
3906 	    else
3907 		coordScreen.Y = sy;
3908 	    if (sx < xSize)
3909 		coordScreen.X = xSize;
3910 	    else
3911 		coordScreen.X = sx;
3912 	    SetConsoleScreenBufferSize(hConsole, coordScreen);
3913 	}
3914     }
3915 
3916     if (!SetConsoleWindowInfo(g_hConOut, TRUE, &srWindowRect))
3917     {
3918 #ifdef MCH_WRITE_DUMP
3919 	if (fdDump)
3920 	{
3921 	    fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
3922 		    GetLastError());
3923 	    fflush(fdDump);
3924 	}
3925 #endif
3926     }
3927 
3928     /* define the new console buffer size */
3929     coordScreen.X = xSize;
3930     coordScreen.Y = ySize;
3931 
3932     if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
3933     {
3934 #ifdef MCH_WRITE_DUMP
3935 	if (fdDump)
3936 	{
3937 	    fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
3938 		    GetLastError());
3939 	    fflush(fdDump);
3940 	}
3941 #endif
3942     }
3943 }
3944 
3945 
3946 /*
3947  * Set the console window to `Rows' * `Columns'
3948  */
3949     void
3950 mch_set_shellsize(void)
3951 {
3952     COORD coordScreen;
3953 
3954     /* Don't change window size while still starting up */
3955     if (suppress_winsize != 0)
3956     {
3957 	suppress_winsize = 2;
3958 	return;
3959     }
3960 
3961     if (term_console)
3962     {
3963 	coordScreen = GetLargestConsoleWindowSize(g_hConOut);
3964 
3965 	/* Clamp Rows and Columns to reasonable values */
3966 	if (Rows > coordScreen.Y)
3967 	    Rows = coordScreen.Y;
3968 	if (Columns > coordScreen.X)
3969 	    Columns = coordScreen.X;
3970 
3971 	ResizeConBufAndWindow(g_hConOut, Columns, Rows);
3972     }
3973 }
3974 
3975 /*
3976  * Rows and/or Columns has changed.
3977  */
3978     void
3979 mch_new_shellsize(void)
3980 {
3981     set_scroll_region(0, 0, Columns - 1, Rows - 1);
3982 }
3983 
3984 
3985 /*
3986  * Called when started up, to set the winsize that was delayed.
3987  */
3988     void
3989 mch_set_winsize_now(void)
3990 {
3991     if (suppress_winsize == 2)
3992     {
3993 	suppress_winsize = 0;
3994 	mch_set_shellsize();
3995 	shell_resized();
3996     }
3997     suppress_winsize = 0;
3998 }
3999 #endif /* FEAT_GUI_W32 */
4000 
4001     static BOOL
4002 vim_create_process(
4003     char		*cmd,
4004     BOOL		inherit_handles,
4005     DWORD		flags,
4006     STARTUPINFO		*si,
4007     PROCESS_INFORMATION *pi)
4008 {
4009 #  ifdef FEAT_MBYTE
4010     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4011     {
4012 	WCHAR	*wcmd = enc_to_utf16(cmd, NULL);
4013 
4014 	if (wcmd != NULL)
4015 	{
4016 	    BOOL ret;
4017 	    ret = CreateProcessW(
4018 		NULL,			/* Executable name */
4019 		wcmd,			/* Command to execute */
4020 		NULL,			/* Process security attributes */
4021 		NULL,			/* Thread security attributes */
4022 		inherit_handles,	/* Inherit handles */
4023 		flags,			/* Creation flags */
4024 		NULL,			/* Environment */
4025 		NULL,			/* Current directory */
4026 		(LPSTARTUPINFOW)si,	/* Startup information */
4027 		pi);			/* Process information */
4028 	    vim_free(wcmd);
4029 	    return ret;
4030 	}
4031     }
4032 #endif
4033     return CreateProcess(
4034 	NULL,			/* Executable name */
4035 	cmd,			/* Command to execute */
4036 	NULL,			/* Process security attributes */
4037 	NULL,			/* Thread security attributes */
4038 	inherit_handles,	/* Inherit handles */
4039 	flags,			/* Creation flags */
4040 	NULL,			/* Environment */
4041 	NULL,			/* Current directory */
4042 	si,			/* Startup information */
4043 	pi);			/* Process information */
4044 }
4045 
4046 
4047 #if defined(FEAT_GUI_W32) || defined(PROTO)
4048 
4049 /*
4050  * Specialised version of system() for Win32 GUI mode.
4051  * This version proceeds as follows:
4052  *    1. Create a console window for use by the subprocess
4053  *    2. Run the subprocess (it gets the allocated console by default)
4054  *    3. Wait for the subprocess to terminate and get its exit code
4055  *    4. Prompt the user to press a key to close the console window
4056  */
4057     static int
4058 mch_system_classic(char *cmd, int options)
4059 {
4060     STARTUPINFO		si;
4061     PROCESS_INFORMATION pi;
4062     DWORD		ret = 0;
4063     HWND		hwnd = GetFocus();
4064 
4065     si.cb = sizeof(si);
4066     si.lpReserved = NULL;
4067     si.lpDesktop = NULL;
4068     si.lpTitle = NULL;
4069     si.dwFlags = STARTF_USESHOWWINDOW;
4070     /*
4071      * It's nicer to run a filter command in a minimized window, but in
4072      * Windows 95 this makes the command MUCH slower.  We can't do it under
4073      * Win32s either as it stops the synchronous spawn workaround working.
4074      * Don't activate the window to keep focus on Vim.
4075      */
4076     if ((options & SHELL_DOOUT) && !mch_windows95() && !gui_is_win32s())
4077 	si.wShowWindow = SW_SHOWMINNOACTIVE;
4078     else
4079 	si.wShowWindow = SW_SHOWNORMAL;
4080     si.cbReserved2 = 0;
4081     si.lpReserved2 = NULL;
4082 
4083     /* There is a strange error on Windows 95 when using "c:\\command.com".
4084      * When the "c:\\" is left out it works OK...? */
4085     if (mch_windows95()
4086 	    && (STRNICMP(cmd, "c:/command.com", 14) == 0
4087 		|| STRNICMP(cmd, "c:\\command.com", 14) == 0))
4088 	cmd += 3;
4089 
4090     /* Now, run the command */
4091     vim_create_process(cmd, FALSE,
4092 	    CREATE_DEFAULT_ERROR_MODE |	CREATE_NEW_CONSOLE, &si, &pi);
4093 
4094     /* Wait for the command to terminate before continuing */
4095     if (g_PlatformId != VER_PLATFORM_WIN32s)
4096     {
4097 #ifdef FEAT_GUI
4098 	int	    delay = 1;
4099 
4100 	/* Keep updating the window while waiting for the shell to finish. */
4101 	for (;;)
4102 	{
4103 	    MSG	msg;
4104 
4105 	    if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
4106 	    {
4107 		TranslateMessage(&msg);
4108 		pDispatchMessage(&msg);
4109 		delay = 1;
4110 		continue;
4111 	    }
4112 	    if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4113 		break;
4114 
4115 	    /* We start waiting for a very short time and then increase it, so
4116 	     * that we respond quickly when the process is quick, and don't
4117 	     * consume too much overhead when it's slow. */
4118 	    if (delay < 50)
4119 		delay += 10;
4120 	}
4121 #else
4122 	WaitForSingleObject(pi.hProcess, INFINITE);
4123 #endif
4124 
4125 	/* Get the command exit code */
4126 	GetExitCodeProcess(pi.hProcess, &ret);
4127     }
4128     else
4129     {
4130 	/*
4131 	 * This ugly code is the only quick way of performing
4132 	 * a synchronous spawn under Win32s. Yuk.
4133 	 */
4134 	num_windows = 0;
4135 	EnumWindows(win32ssynch_cb, 0);
4136 	old_num_windows = num_windows;
4137 	do
4138 	{
4139 	    Sleep(1000);
4140 	    num_windows = 0;
4141 	    EnumWindows(win32ssynch_cb, 0);
4142 	} while (num_windows == old_num_windows);
4143 	ret = 0;
4144     }
4145 
4146     /* Close the handles to the subprocess, so that it goes away */
4147     CloseHandle(pi.hThread);
4148     CloseHandle(pi.hProcess);
4149 
4150     /* Try to get input focus back.  Doesn't always work though. */
4151     PostMessage(hwnd, WM_SETFOCUS, 0, 0);
4152 
4153     return ret;
4154 }
4155 
4156 /*
4157  * Thread launched by the gui to send the current buffer data to the
4158  * process. This way avoid to hang up vim totally if the children
4159  * process take a long time to process the lines.
4160  */
4161     static DWORD WINAPI
4162 sub_process_writer(LPVOID param)
4163 {
4164     HANDLE	    g_hChildStd_IN_Wr = param;
4165     linenr_T	    lnum = curbuf->b_op_start.lnum;
4166     DWORD	    len = 0;
4167     DWORD	    l;
4168     char_u	    *lp = ml_get(lnum);
4169     char_u	    *s;
4170     int		    written = 0;
4171 
4172     for (;;)
4173     {
4174 	l = (DWORD)STRLEN(lp + written);
4175 	if (l == 0)
4176 	    len = 0;
4177 	else if (lp[written] == NL)
4178 	{
4179 	    /* NL -> NUL translation */
4180 	    WriteFile(g_hChildStd_IN_Wr, "", 1, &len, NULL);
4181 	}
4182 	else
4183 	{
4184 	    s = vim_strchr(lp + written, NL);
4185 	    WriteFile(g_hChildStd_IN_Wr, (char *)lp + written,
4186 		      s == NULL ? l : (DWORD)(s - (lp + written)),
4187 		      &len, NULL);
4188 	}
4189 	if (len == (int)l)
4190 	{
4191 	    /* Finished a line, add a NL, unless this line should not have
4192 	     * one. */
4193 	    if (lnum != curbuf->b_op_end.lnum
4194 		|| (!curbuf->b_p_bin
4195 		    && curbuf->b_p_fixeol)
4196 		|| (lnum != curbuf->b_no_eol_lnum
4197 		    && (lnum != curbuf->b_ml.ml_line_count
4198 			|| curbuf->b_p_eol)))
4199 	    {
4200 		WriteFile(g_hChildStd_IN_Wr, "\n", 1, (LPDWORD)&ignored, NULL);
4201 	    }
4202 
4203 	    ++lnum;
4204 	    if (lnum > curbuf->b_op_end.lnum)
4205 		break;
4206 
4207 	    lp = ml_get(lnum);
4208 	    written = 0;
4209 	}
4210 	else if (len > 0)
4211 	    written += len;
4212     }
4213 
4214     /* finished all the lines, close pipe */
4215     CloseHandle(g_hChildStd_IN_Wr);
4216     ExitThread(0);
4217 }
4218 
4219 
4220 # define BUFLEN 100	/* length for buffer, stolen from unix version */
4221 
4222 /*
4223  * This function read from the children's stdout and write the
4224  * data on screen or in the buffer accordingly.
4225  */
4226     static void
4227 dump_pipe(int	    options,
4228 	  HANDLE    g_hChildStd_OUT_Rd,
4229 	  garray_T  *ga,
4230 	  char_u    buffer[],
4231 	  DWORD	    *buffer_off)
4232 {
4233     DWORD	availableBytes = 0;
4234     DWORD	i;
4235     int		ret;
4236     DWORD	len;
4237     DWORD	toRead;
4238     int		repeatCount;
4239 
4240     /* we query the pipe to see if there is any data to read
4241      * to avoid to perform a blocking read */
4242     ret = PeekNamedPipe(g_hChildStd_OUT_Rd, /* pipe to query */
4243 			NULL,		    /* optional buffer */
4244 			0,		    /* buffer size */
4245 			NULL,		    /* number of read bytes */
4246 			&availableBytes,    /* available bytes total */
4247 			NULL);		    /* byteLeft */
4248 
4249     repeatCount = 0;
4250     /* We got real data in the pipe, read it */
4251     while (ret != 0 && availableBytes > 0)
4252     {
4253 	repeatCount++;
4254 	toRead =
4255 # ifdef FEAT_MBYTE
4256 		 (DWORD)(BUFLEN - *buffer_off);
4257 # else
4258 		 (DWORD)BUFLEN;
4259 # endif
4260 	toRead = availableBytes < toRead ? availableBytes : toRead;
4261 	ReadFile(g_hChildStd_OUT_Rd, buffer
4262 # ifdef FEAT_MBYTE
4263 		 + *buffer_off, toRead
4264 # else
4265 		 , toRead
4266 # endif
4267 		 , &len, NULL);
4268 
4269 	/* If we haven't read anything, there is a problem */
4270 	if (len == 0)
4271 	    break;
4272 
4273 	availableBytes -= len;
4274 
4275 	if (options & SHELL_READ)
4276 	{
4277 	    /* Do NUL -> NL translation, append NL separated
4278 	     * lines to the current buffer. */
4279 	    for (i = 0; i < len; ++i)
4280 	    {
4281 		if (buffer[i] == NL)
4282 		    append_ga_line(ga);
4283 		else if (buffer[i] == NUL)
4284 		    ga_append(ga, NL);
4285 		else
4286 		    ga_append(ga, buffer[i]);
4287 	    }
4288 	}
4289 # ifdef FEAT_MBYTE
4290 	else if (has_mbyte)
4291 	{
4292 	    int		l;
4293 	    int		c;
4294 	    char_u	*p;
4295 
4296 	    len += *buffer_off;
4297 	    buffer[len] = NUL;
4298 
4299 	    /* Check if the last character in buffer[] is
4300 	     * incomplete, keep these bytes for the next
4301 	     * round. */
4302 	    for (p = buffer; p < buffer + len; p += l)
4303 	    {
4304 		l = mb_cptr2len(p);
4305 		if (l == 0)
4306 		    l = 1;  /* NUL byte? */
4307 		else if (MB_BYTE2LEN(*p) != l)
4308 		    break;
4309 	    }
4310 	    if (p == buffer)	/* no complete character */
4311 	    {
4312 		/* avoid getting stuck at an illegal byte */
4313 		if (len >= 12)
4314 		    ++p;
4315 		else
4316 		{
4317 		    *buffer_off = len;
4318 		    return;
4319 		}
4320 	    }
4321 	    c = *p;
4322 	    *p = NUL;
4323 	    msg_puts(buffer);
4324 	    if (p < buffer + len)
4325 	    {
4326 		*p = c;
4327 		*buffer_off = (DWORD)((buffer + len) - p);
4328 		mch_memmove(buffer, p, *buffer_off);
4329 		return;
4330 	    }
4331 	    *buffer_off = 0;
4332 	}
4333 # endif /* FEAT_MBYTE */
4334 	else
4335 	{
4336 	    buffer[len] = NUL;
4337 	    msg_puts(buffer);
4338 	}
4339 
4340 	windgoto(msg_row, msg_col);
4341 	cursor_on();
4342 	out_flush();
4343     }
4344 }
4345 
4346 /*
4347  * Version of system to use for windows NT > 5.0 (Win2K), use pipe
4348  * for communication and doesn't open any new window.
4349  */
4350     static int
4351 mch_system_piped(char *cmd, int options)
4352 {
4353     STARTUPINFO		si;
4354     PROCESS_INFORMATION pi;
4355     DWORD		ret = 0;
4356 
4357     HANDLE g_hChildStd_IN_Rd = NULL;
4358     HANDLE g_hChildStd_IN_Wr = NULL;
4359     HANDLE g_hChildStd_OUT_Rd = NULL;
4360     HANDLE g_hChildStd_OUT_Wr = NULL;
4361 
4362     char_u	buffer[BUFLEN + 1]; /* reading buffer + size */
4363     DWORD	len;
4364 
4365     /* buffer used to receive keys */
4366     char_u	ta_buf[BUFLEN + 1];	/* TypeAHead */
4367     int		ta_len = 0;		/* valid bytes in ta_buf[] */
4368 
4369     DWORD	i;
4370     int		c;
4371     int		noread_cnt = 0;
4372     garray_T	ga;
4373     int	    delay = 1;
4374     DWORD	buffer_off = 0;	/* valid bytes in buffer[] */
4375     char	*p = NULL;
4376 
4377     SECURITY_ATTRIBUTES saAttr;
4378 
4379     /* Set the bInheritHandle flag so pipe handles are inherited. */
4380     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
4381     saAttr.bInheritHandle = TRUE;
4382     saAttr.lpSecurityDescriptor = NULL;
4383 
4384     if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)
4385 	/* Ensure the read handle to the pipe for STDOUT is not inherited. */
4386        || ! pSetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)
4387 	/* Create a pipe for the child process's STDIN. */
4388        || ! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)
4389 	/* Ensure the write handle to the pipe for STDIN is not inherited. */
4390        || ! pSetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
4391     {
4392 	CloseHandle(g_hChildStd_IN_Rd);
4393 	CloseHandle(g_hChildStd_IN_Wr);
4394 	CloseHandle(g_hChildStd_OUT_Rd);
4395 	CloseHandle(g_hChildStd_OUT_Wr);
4396 	MSG_PUTS(_("\nCannot create pipes\n"));
4397     }
4398 
4399     si.cb = sizeof(si);
4400     si.lpReserved = NULL;
4401     si.lpDesktop = NULL;
4402     si.lpTitle = NULL;
4403     si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
4404 
4405     /* set-up our file redirection */
4406     si.hStdError = g_hChildStd_OUT_Wr;
4407     si.hStdOutput = g_hChildStd_OUT_Wr;
4408     si.hStdInput = g_hChildStd_IN_Rd;
4409     si.wShowWindow = SW_HIDE;
4410     si.cbReserved2 = 0;
4411     si.lpReserved2 = NULL;
4412 
4413     if (options & SHELL_READ)
4414 	ga_init2(&ga, 1, BUFLEN);
4415 
4416     if (cmd != NULL)
4417     {
4418 	p = (char *)vim_strsave((char_u *)cmd);
4419 	if (p != NULL)
4420 	    unescape_shellxquote((char_u *)p, p_sxe);
4421 	else
4422 	    p = cmd;
4423     }
4424 
4425     /* Now, run the command.
4426      * About "Inherit handles" being TRUE: this command can be litigious,
4427      * handle inheritance was deactivated for pending temp file, but, if we
4428      * deactivate it, the pipes don't work for some reason. */
4429      vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE, &si, &pi);
4430 
4431     if (p != cmd)
4432 	vim_free(p);
4433 
4434     /* Close our unused side of the pipes */
4435     CloseHandle(g_hChildStd_IN_Rd);
4436     CloseHandle(g_hChildStd_OUT_Wr);
4437 
4438     if (options & SHELL_WRITE)
4439     {
4440 	HANDLE thread =
4441 	   CreateThread(NULL,  /* security attributes */
4442 			0,     /* default stack size */
4443 			sub_process_writer, /* function to be executed */
4444 			g_hChildStd_IN_Wr,  /* parameter */
4445 			0,		 /* creation flag, start immediately */
4446 			NULL);		    /* we don't care about thread id */
4447 	CloseHandle(thread);
4448 	g_hChildStd_IN_Wr = NULL;
4449     }
4450 
4451     /* Keep updating the window while waiting for the shell to finish. */
4452     for (;;)
4453     {
4454 	MSG	msg;
4455 
4456 	if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
4457 	{
4458 	    TranslateMessage(&msg);
4459 	    pDispatchMessage(&msg);
4460 	}
4461 
4462 	/* write pipe information in the window */
4463 	if ((options & (SHELL_READ|SHELL_WRITE))
4464 # ifdef FEAT_GUI
4465 		|| gui.in_use
4466 # endif
4467 	    )
4468 	{
4469 	    len = 0;
4470 	    if (!(options & SHELL_EXPAND)
4471 		&& ((options &
4472 			(SHELL_READ|SHELL_WRITE|SHELL_COOKED))
4473 		    != (SHELL_READ|SHELL_WRITE|SHELL_COOKED)
4474 # ifdef FEAT_GUI
4475 		    || gui.in_use
4476 # endif
4477 		    )
4478 		&& (ta_len > 0 || noread_cnt > 4))
4479 	    {
4480 		if (ta_len == 0)
4481 		{
4482 		    /* Get extra characters when we don't have any.  Reset the
4483 		     * counter and timer. */
4484 		    noread_cnt = 0;
4485 # if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
4486 		    gettimeofday(&start_tv, NULL);
4487 # endif
4488 		    len = ui_inchar(ta_buf, BUFLEN, 10L, 0);
4489 		}
4490 		if (ta_len > 0 || len > 0)
4491 		{
4492 		    /*
4493 		     * For pipes: Check for CTRL-C: send interrupt signal to
4494 		     * child.  Check for CTRL-D: EOF, close pipe to child.
4495 		     */
4496 		    if (len == 1 && cmd != NULL)
4497 		    {
4498 			if (ta_buf[ta_len] == Ctrl_C)
4499 			{
4500 			    /* Learn what exit code is expected, for
4501 				* now put 9 as SIGKILL */
4502 			    TerminateProcess(pi.hProcess, 9);
4503 			}
4504 			if (ta_buf[ta_len] == Ctrl_D)
4505 			{
4506 			    CloseHandle(g_hChildStd_IN_Wr);
4507 			    g_hChildStd_IN_Wr = NULL;
4508 			}
4509 		    }
4510 
4511 		    /* replace K_BS by <BS> and K_DEL by <DEL> */
4512 		    for (i = ta_len; i < ta_len + len; ++i)
4513 		    {
4514 			if (ta_buf[i] == CSI && len - i > 2)
4515 			{
4516 			    c = TERMCAP2KEY(ta_buf[i + 1], ta_buf[i + 2]);
4517 			    if (c == K_DEL || c == K_KDEL || c == K_BS)
4518 			    {
4519 				mch_memmove(ta_buf + i + 1, ta_buf + i + 3,
4520 					    (size_t)(len - i - 2));
4521 				if (c == K_DEL || c == K_KDEL)
4522 				    ta_buf[i] = DEL;
4523 				else
4524 				    ta_buf[i] = Ctrl_H;
4525 				len -= 2;
4526 			    }
4527 			}
4528 			else if (ta_buf[i] == '\r')
4529 			    ta_buf[i] = '\n';
4530 # ifdef FEAT_MBYTE
4531 			if (has_mbyte)
4532 			    i += (*mb_ptr2len_len)(ta_buf + i,
4533 						    ta_len + len - i) - 1;
4534 # endif
4535 		    }
4536 
4537 		    /*
4538 		     * For pipes: echo the typed characters.  For a pty this
4539 		     * does not seem to work.
4540 		     */
4541 		    for (i = ta_len; i < ta_len + len; ++i)
4542 		    {
4543 			if (ta_buf[i] == '\n' || ta_buf[i] == '\b')
4544 			    msg_putchar(ta_buf[i]);
4545 # ifdef FEAT_MBYTE
4546 			else if (has_mbyte)
4547 			{
4548 			    int l = (*mb_ptr2len)(ta_buf + i);
4549 
4550 			    msg_outtrans_len(ta_buf + i, l);
4551 			    i += l - 1;
4552 			}
4553 # endif
4554 			else
4555 			    msg_outtrans_len(ta_buf + i, 1);
4556 		    }
4557 		    windgoto(msg_row, msg_col);
4558 		    out_flush();
4559 
4560 		    ta_len += len;
4561 
4562 		    /*
4563 		     * Write the characters to the child, unless EOF has been
4564 		     * typed for pipes.  Write one character at a time, to
4565 		     * avoid losing too much typeahead.  When writing buffer
4566 		     * lines, drop the typed characters (only check for
4567 		     * CTRL-C).
4568 		     */
4569 		    if (options & SHELL_WRITE)
4570 			ta_len = 0;
4571 		    else if (g_hChildStd_IN_Wr != NULL)
4572 		    {
4573 			WriteFile(g_hChildStd_IN_Wr, (char*)ta_buf,
4574 				    1, &len, NULL);
4575 			// if we are typing in, we want to keep things reactive
4576 			delay = 1;
4577 			if (len > 0)
4578 			{
4579 			    ta_len -= len;
4580 			    mch_memmove(ta_buf, ta_buf + len, ta_len);
4581 			}
4582 		    }
4583 		}
4584 	    }
4585 	}
4586 
4587 	if (ta_len)
4588 	    ui_inchar_undo(ta_buf, ta_len);
4589 
4590 	if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4591 	{
4592 	    dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
4593 	    break;
4594 	}
4595 
4596 	++noread_cnt;
4597 	dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
4598 
4599 	/* We start waiting for a very short time and then increase it, so
4600 	 * that we respond quickly when the process is quick, and don't
4601 	 * consume too much overhead when it's slow. */
4602 	if (delay < 50)
4603 	    delay += 10;
4604     }
4605 
4606     /* Close the pipe */
4607     CloseHandle(g_hChildStd_OUT_Rd);
4608     if (g_hChildStd_IN_Wr != NULL)
4609 	CloseHandle(g_hChildStd_IN_Wr);
4610 
4611     WaitForSingleObject(pi.hProcess, INFINITE);
4612 
4613     /* Get the command exit code */
4614     GetExitCodeProcess(pi.hProcess, &ret);
4615 
4616     if (options & SHELL_READ)
4617     {
4618 	if (ga.ga_len > 0)
4619 	{
4620 	    append_ga_line(&ga);
4621 	    /* remember that the NL was missing */
4622 	    curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
4623 	}
4624 	else
4625 	    curbuf->b_no_eol_lnum = 0;
4626 	ga_clear(&ga);
4627     }
4628 
4629     /* Close the handles to the subprocess, so that it goes away */
4630     CloseHandle(pi.hThread);
4631     CloseHandle(pi.hProcess);
4632 
4633     return ret;
4634 }
4635 
4636     static int
4637 mch_system(char *cmd, int options)
4638 {
4639     /* if we can pipe and the shelltemp option is off */
4640     if (allowPiping && !p_stmp)
4641 	return mch_system_piped(cmd, options);
4642     else
4643 	return mch_system_classic(cmd, options);
4644 }
4645 #else
4646 
4647 # ifdef FEAT_MBYTE
4648     static int
4649 mch_system(char *cmd, int options)
4650 {
4651     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4652     {
4653 	WCHAR	*wcmd = enc_to_utf16(cmd, NULL);
4654 	if (wcmd != NULL)
4655 	{
4656 	    int ret = _wsystem(wcmd);
4657 	    vim_free(wcmd);
4658 	    return ret;
4659 	}
4660     }
4661     return system(cmd);
4662 }
4663 # else
4664 #  define mch_system(c, o) system(c)
4665 # endif
4666 
4667 #endif
4668 
4669 /*
4670  * Either execute a command by calling the shell or start a new shell
4671  */
4672     int
4673 mch_call_shell(
4674     char_u  *cmd,
4675     int	    options)	/* SHELL_*, see vim.h */
4676 {
4677     int		x = 0;
4678     int		tmode = cur_tmode;
4679 #ifdef FEAT_TITLE
4680     char	szShellTitle[512];
4681 # ifdef FEAT_MBYTE
4682     int		did_set_title = FALSE;
4683 
4684     /* Change the title to reflect that we are in a subshell. */
4685     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4686     {
4687 	WCHAR szShellTitle[512];
4688 
4689 	if (GetConsoleTitleW(szShellTitle,
4690 				  sizeof(szShellTitle)/sizeof(WCHAR) - 4) > 0)
4691 	{
4692 	    if (cmd == NULL)
4693 		wcscat(szShellTitle, L" :sh");
4694 	    else
4695 	    {
4696 		WCHAR *wn = enc_to_utf16(cmd, NULL);
4697 
4698 		if (wn != NULL)
4699 		{
4700 		    wcscat(szShellTitle, L" - !");
4701 		    if ((wcslen(szShellTitle) + wcslen(wn) <
4702 					  sizeof(szShellTitle)/sizeof(WCHAR)))
4703 			wcscat(szShellTitle, wn);
4704 		    SetConsoleTitleW(szShellTitle);
4705 		    vim_free(wn);
4706 		    did_set_title = TRUE;
4707 		}
4708 	    }
4709 	}
4710     }
4711     if (!did_set_title)
4712 # endif
4713 	/* Change the title to reflect that we are in a subshell. */
4714 	if (GetConsoleTitle(szShellTitle, sizeof(szShellTitle) - 4) > 0)
4715 	{
4716 	    if (cmd == NULL)
4717 		strcat(szShellTitle, " :sh");
4718 	    else
4719 	    {
4720 		strcat(szShellTitle, " - !");
4721 		if ((strlen(szShellTitle) + strlen(cmd) < sizeof(szShellTitle)))
4722 		    strcat(szShellTitle, cmd);
4723 	    }
4724 	    SetConsoleTitle(szShellTitle);
4725 	}
4726 #endif
4727 
4728     out_flush();
4729 
4730 #ifdef MCH_WRITE_DUMP
4731     if (fdDump)
4732     {
4733 	fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
4734 	fflush(fdDump);
4735     }
4736 #endif
4737 
4738     /*
4739      * Catch all deadly signals while running the external command, because a
4740      * CTRL-C, Ctrl-Break or illegal instruction  might otherwise kill us.
4741      */
4742     signal(SIGINT, SIG_IGN);
4743 #if defined(__GNUC__) && !defined(__MINGW32__)
4744     signal(SIGKILL, SIG_IGN);
4745 #else
4746     signal(SIGBREAK, SIG_IGN);
4747 #endif
4748     signal(SIGILL, SIG_IGN);
4749     signal(SIGFPE, SIG_IGN);
4750     signal(SIGSEGV, SIG_IGN);
4751     signal(SIGTERM, SIG_IGN);
4752     signal(SIGABRT, SIG_IGN);
4753 
4754     if (options & SHELL_COOKED)
4755 	settmode(TMODE_COOK);	/* set to normal mode */
4756 
4757     if (cmd == NULL)
4758     {
4759 	x = mch_system(p_sh, options);
4760     }
4761     else
4762     {
4763 	/* we use "command" or "cmd" to start the shell; slow but easy */
4764 	char_u	*newcmd = NULL;
4765 	char_u	*cmdbase = cmd;
4766 	long_u	cmdlen;
4767 
4768 	/* Skip a leading ", ( and "(. */
4769 	if (*cmdbase == '"' )
4770 	    ++cmdbase;
4771 	if (*cmdbase == '(')
4772 	    ++cmdbase;
4773 
4774 	if ((STRNICMP(cmdbase, "start", 5) == 0) && vim_iswhite(cmdbase[5]))
4775 	{
4776 	    STARTUPINFO		si;
4777 	    PROCESS_INFORMATION	pi;
4778 	    DWORD		flags = CREATE_NEW_CONSOLE;
4779 	    char_u		*p;
4780 
4781 	    ZeroMemory(&si, sizeof(si));
4782 	    si.cb = sizeof(si);
4783 	    si.lpReserved = NULL;
4784 	    si.lpDesktop = NULL;
4785 	    si.lpTitle = NULL;
4786 	    si.dwFlags = 0;
4787 	    si.cbReserved2 = 0;
4788 	    si.lpReserved2 = NULL;
4789 
4790 	    cmdbase = skipwhite(cmdbase + 5);
4791 	    if ((STRNICMP(cmdbase, "/min", 4) == 0)
4792 		    && vim_iswhite(cmdbase[4]))
4793 	    {
4794 		cmdbase = skipwhite(cmdbase + 4);
4795 		si.dwFlags = STARTF_USESHOWWINDOW;
4796 		si.wShowWindow = SW_SHOWMINNOACTIVE;
4797 	    }
4798 	    else if ((STRNICMP(cmdbase, "/b", 2) == 0)
4799 		    && vim_iswhite(cmdbase[2]))
4800 	    {
4801 		cmdbase = skipwhite(cmdbase + 2);
4802 		flags = CREATE_NO_WINDOW;
4803 		si.dwFlags = STARTF_USESTDHANDLES;
4804 		si.hStdInput = CreateFile("\\\\.\\NUL",	// File name
4805 		    GENERIC_READ,			// Access flags
4806 		    0,					// Share flags
4807 		    NULL,				// Security att.
4808 		    OPEN_EXISTING,			// Open flags
4809 		    FILE_ATTRIBUTE_NORMAL,		// File att.
4810 		    NULL);				// Temp file
4811 		si.hStdOutput = si.hStdInput;
4812 		si.hStdError = si.hStdInput;
4813 	    }
4814 
4815 	    /* Remove a trailing ", ) and )" if they have a match
4816 	     * at the start of the command. */
4817 	    if (cmdbase > cmd)
4818 	    {
4819 		p = cmdbase + STRLEN(cmdbase);
4820 		if (p > cmdbase && p[-1] == '"' && *cmd == '"')
4821 		    *--p = NUL;
4822 		if (p > cmdbase && p[-1] == ')'
4823 			&& (*cmd =='(' || cmd[1] == '('))
4824 		    *--p = NUL;
4825 	    }
4826 
4827 	    newcmd = cmdbase;
4828 	    unescape_shellxquote(cmdbase, p_sxe);
4829 
4830 	    /*
4831 	     * If creating new console, arguments are passed to the
4832 	     * 'cmd.exe' as-is. If it's not, arguments are not treated
4833 	     * correctly for current 'cmd.exe'. So unescape characters in
4834 	     * shellxescape except '|' for avoiding to be treated as
4835 	     * argument to them. Pass the arguments to sub-shell.
4836 	     */
4837 	    if (flags != CREATE_NEW_CONSOLE)
4838 	    {
4839 		char_u	*subcmd;
4840 		char_u	*cmd_shell = mch_getenv("COMSPEC");
4841 
4842 		if (cmd_shell == NULL || *cmd_shell == NUL)
4843 		    cmd_shell = default_shell();
4844 
4845 		subcmd = vim_strsave_escaped_ext(cmdbase, "|", '^', FALSE);
4846 		if (subcmd != NULL)
4847 		{
4848 		    /* make "cmd.exe /c arguments" */
4849 		    cmdlen = STRLEN(cmd_shell) + STRLEN(subcmd) + 5;
4850 		    newcmd = lalloc(cmdlen, TRUE);
4851 		    if (newcmd != NULL)
4852 			vim_snprintf((char *)newcmd, cmdlen, "%s /c %s",
4853 						       cmd_shell, subcmd);
4854 		    else
4855 			newcmd = cmdbase;
4856 		    vim_free(subcmd);
4857 		}
4858 	    }
4859 
4860 	    /*
4861 	     * Now, start the command as a process, so that it doesn't
4862 	     * inherit our handles which causes unpleasant dangling swap
4863 	     * files if we exit before the spawned process
4864 	     */
4865 	    if (vim_create_process(newcmd, FALSE, flags, &si, &pi))
4866 		x = 0;
4867 	    else
4868 	    {
4869 		x = -1;
4870 #ifdef FEAT_GUI_W32
4871 		EMSG(_("E371: Command not found"));
4872 #endif
4873 	    }
4874 
4875 	    if (newcmd != cmdbase)
4876 		vim_free(newcmd);
4877 
4878 	    if (si.dwFlags == STARTF_USESTDHANDLES && si.hStdInput != NULL)
4879 	    {
4880 		/* Close the handle to \\.\NUL created above. */
4881 		CloseHandle(si.hStdInput);
4882 	    }
4883 	    /* Close the handles to the subprocess, so that it goes away */
4884 	    CloseHandle(pi.hThread);
4885 	    CloseHandle(pi.hProcess);
4886 	}
4887 	else
4888 	{
4889 	    cmdlen = (
4890 #ifdef FEAT_GUI_W32
4891 		(allowPiping && !p_stmp ? 0 : STRLEN(vimrun_path)) +
4892 #endif
4893 		STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10);
4894 
4895 	    newcmd = lalloc(cmdlen, TRUE);
4896 	    if (newcmd != NULL)
4897 	    {
4898 #if defined(FEAT_GUI_W32)
4899 		if (need_vimrun_warning)
4900 		{
4901 		    MessageBox(NULL,
4902 			    _("VIMRUN.EXE not found in your $PATH.\n"
4903 				"External commands will not pause after completion.\n"
4904 				"See  :help win32-vimrun  for more information."),
4905 			    _("Vim Warning"),
4906 			    MB_ICONWARNING);
4907 		    need_vimrun_warning = FALSE;
4908 		}
4909 		if (!s_dont_use_vimrun && (!allowPiping || p_stmp))
4910 		    /* Use vimrun to execute the command.  It opens a console
4911 		     * window, which can be closed without killing Vim. */
4912 		    vim_snprintf((char *)newcmd, cmdlen, "%s%s%s %s %s",
4913 			    vimrun_path,
4914 			    (msg_silent != 0 || (options & SHELL_DOOUT))
4915 								 ? "-s " : "",
4916 			    p_sh, p_shcf, cmd);
4917 		else
4918 #endif
4919 		    vim_snprintf((char *)newcmd, cmdlen, "%s %s %s",
4920 							   p_sh, p_shcf, cmd);
4921 		x = mch_system((char *)newcmd, options);
4922 		vim_free(newcmd);
4923 	    }
4924 	}
4925     }
4926 
4927     if (tmode == TMODE_RAW)
4928 	settmode(TMODE_RAW);	/* set to raw mode */
4929 
4930     /* Print the return value, unless "vimrun" was used. */
4931     if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent
4932 #if defined(FEAT_GUI_W32)
4933 		&& ((options & SHELL_DOOUT) || s_dont_use_vimrun
4934 						  || (allowPiping && !p_stmp))
4935 #endif
4936 	    )
4937     {
4938 	smsg(_("shell returned %d"), x);
4939 	msg_putchar('\n');
4940     }
4941 #ifdef FEAT_TITLE
4942     resettitle();
4943 #endif
4944 
4945     signal(SIGINT, SIG_DFL);
4946 #if defined(__GNUC__) && !defined(__MINGW32__)
4947     signal(SIGKILL, SIG_DFL);
4948 #else
4949     signal(SIGBREAK, SIG_DFL);
4950 #endif
4951     signal(SIGILL, SIG_DFL);
4952     signal(SIGFPE, SIG_DFL);
4953     signal(SIGSEGV, SIG_DFL);
4954     signal(SIGTERM, SIG_DFL);
4955     signal(SIGABRT, SIG_DFL);
4956 
4957     return x;
4958 }
4959 
4960 
4961 #ifndef FEAT_GUI_W32
4962 
4963 /*
4964  * Start termcap mode
4965  */
4966     static void
4967 termcap_mode_start(void)
4968 {
4969     DWORD cmodein;
4970 
4971     if (g_fTermcapMode)
4972 	return;
4973 
4974     SaveConsoleBuffer(&g_cbNonTermcap);
4975 
4976     if (g_cbTermcap.IsValid)
4977     {
4978 	/*
4979 	 * We've been in termcap mode before.  Restore certain screen
4980 	 * characteristics, including the buffer size and the window
4981 	 * size.  Since we will be redrawing the screen, we don't need
4982 	 * to restore the actual contents of the buffer.
4983 	 */
4984 	RestoreConsoleBuffer(&g_cbTermcap, FALSE);
4985 	SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow);
4986 	Rows = g_cbTermcap.Info.dwSize.Y;
4987 	Columns = g_cbTermcap.Info.dwSize.X;
4988     }
4989     else
4990     {
4991 	/*
4992 	 * This is our first time entering termcap mode.  Clear the console
4993 	 * screen buffer, and resize the buffer to match the current window
4994 	 * size.  We will use this as the size of our editing environment.
4995 	 */
4996 	ClearConsoleBuffer(g_attrCurrent);
4997 	ResizeConBufAndWindow(g_hConOut, Columns, Rows);
4998     }
4999 
5000 #ifdef FEAT_TITLE
5001     resettitle();
5002 #endif
5003 
5004     GetConsoleMode(g_hConIn, &cmodein);
5005 #ifdef FEAT_MOUSE
5006     if (g_fMouseActive)
5007 	cmodein |= ENABLE_MOUSE_INPUT;
5008     else
5009 	cmodein &= ~ENABLE_MOUSE_INPUT;
5010 #endif
5011     cmodein |= ENABLE_WINDOW_INPUT;
5012     SetConsoleMode(g_hConIn, cmodein);
5013 
5014     redraw_later_clear();
5015     g_fTermcapMode = TRUE;
5016 }
5017 
5018 
5019 /*
5020  * End termcap mode
5021  */
5022     static void
5023 termcap_mode_end(void)
5024 {
5025     DWORD cmodein;
5026     ConsoleBuffer *cb;
5027     COORD coord;
5028     DWORD dwDummy;
5029 
5030     if (!g_fTermcapMode)
5031 	return;
5032 
5033     SaveConsoleBuffer(&g_cbTermcap);
5034 
5035     GetConsoleMode(g_hConIn, &cmodein);
5036     cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
5037     SetConsoleMode(g_hConIn, cmodein);
5038 
5039 #ifdef FEAT_RESTORE_ORIG_SCREEN
5040     cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
5041 #else
5042     cb = &g_cbNonTermcap;
5043 #endif
5044     RestoreConsoleBuffer(cb, p_rs);
5045     SetConsoleCursorInfo(g_hConOut, &g_cci);
5046 
5047     if (p_rs || exiting)
5048     {
5049 	/*
5050 	 * Clear anything that happens to be on the current line.
5051 	 */
5052 	coord.X = 0;
5053 	coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1));
5054 	FillConsoleOutputCharacter(g_hConOut, ' ',
5055 		cb->Info.dwSize.X, coord, &dwDummy);
5056 	/*
5057 	 * The following is just for aesthetics.  If we are exiting without
5058 	 * restoring the screen, then we want to have a prompt string
5059 	 * appear at the bottom line.  However, the command interpreter
5060 	 * seems to always advance the cursor one line before displaying
5061 	 * the prompt string, which causes the screen to scroll.  To
5062 	 * counter this, move the cursor up one line before exiting.
5063 	 */
5064 	if (exiting && !p_rs)
5065 	    coord.Y--;
5066 	/*
5067 	 * Position the cursor at the leftmost column of the desired row.
5068 	 */
5069 	SetConsoleCursorPosition(g_hConOut, coord);
5070     }
5071 
5072     g_fTermcapMode = FALSE;
5073 }
5074 #endif /* FEAT_GUI_W32 */
5075 
5076 
5077 #ifdef FEAT_GUI_W32
5078 /*ARGSUSED*/
5079     void
5080 mch_write(
5081     char_u  *s,
5082     int	    len)
5083 {
5084     /* never used */
5085 }
5086 
5087 #else
5088 
5089 /*
5090  * clear `n' chars, starting from `coord'
5091  */
5092     static void
5093 clear_chars(
5094     COORD coord,
5095     DWORD n)
5096 {
5097     DWORD dwDummy;
5098 
5099     FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
5100     FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy);
5101 }
5102 
5103 
5104 /*
5105  * Clear the screen
5106  */
5107     static void
5108 clear_screen(void)
5109 {
5110     g_coord.X = g_coord.Y = 0;
5111     clear_chars(g_coord, Rows * Columns);
5112 }
5113 
5114 
5115 /*
5116  * Clear to end of display
5117  */
5118     static void
5119 clear_to_end_of_display(void)
5120 {
5121     clear_chars(g_coord, (Rows - g_coord.Y - 1)
5122 					   * Columns + (Columns - g_coord.X));
5123 }
5124 
5125 
5126 /*
5127  * Clear to end of line
5128  */
5129     static void
5130 clear_to_end_of_line(void)
5131 {
5132     clear_chars(g_coord, Columns - g_coord.X);
5133 }
5134 
5135 
5136 /*
5137  * Scroll the scroll region up by `cLines' lines
5138  */
5139     static void
5140 scroll(unsigned cLines)
5141 {
5142     COORD oldcoord = g_coord;
5143 
5144     gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
5145     delete_lines(cLines);
5146 
5147     g_coord = oldcoord;
5148 }
5149 
5150 
5151 /*
5152  * Set the scroll region
5153  */
5154     static void
5155 set_scroll_region(
5156     unsigned left,
5157     unsigned top,
5158     unsigned right,
5159     unsigned bottom)
5160 {
5161     if (left >= right
5162 	    || top >= bottom
5163 	    || right > (unsigned) Columns - 1
5164 	    || bottom > (unsigned) Rows - 1)
5165 	return;
5166 
5167     g_srScrollRegion.Left =   left;
5168     g_srScrollRegion.Top =    top;
5169     g_srScrollRegion.Right =  right;
5170     g_srScrollRegion.Bottom = bottom;
5171 }
5172 
5173 
5174 /*
5175  * Insert `cLines' lines at the current cursor position
5176  */
5177     static void
5178 insert_lines(unsigned cLines)
5179 {
5180     SMALL_RECT	    source;
5181     COORD	    dest;
5182     CHAR_INFO	    fill;
5183 
5184     dest.X = 0;
5185     dest.Y = g_coord.Y + cLines;
5186 
5187     source.Left   = 0;
5188     source.Top	  = g_coord.Y;
5189     source.Right  = g_srScrollRegion.Right;
5190     source.Bottom = g_srScrollRegion.Bottom - cLines;
5191 
5192     fill.Char.AsciiChar = ' ';
5193     fill.Attributes = g_attrCurrent;
5194 
5195     ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
5196 
5197     /* Here we have to deal with a win32 console flake: If the scroll
5198      * region looks like abc and we scroll c to a and fill with d we get
5199      * cbd... if we scroll block c one line at a time to a, we get cdd...
5200      * vim expects cdd consistently... So we have to deal with that
5201      * here... (this also occurs scrolling the same way in the other
5202      * direction).  */
5203 
5204     if (source.Bottom < dest.Y)
5205     {
5206 	COORD coord;
5207 
5208 	coord.X = 0;
5209 	coord.Y = source.Bottom;
5210 	clear_chars(coord, Columns * (dest.Y - source.Bottom));
5211     }
5212 }
5213 
5214 
5215 /*
5216  * Delete `cLines' lines at the current cursor position
5217  */
5218     static void
5219 delete_lines(unsigned cLines)
5220 {
5221     SMALL_RECT	    source;
5222     COORD	    dest;
5223     CHAR_INFO	    fill;
5224     int		    nb;
5225 
5226     dest.X = 0;
5227     dest.Y = g_coord.Y;
5228 
5229     source.Left   = 0;
5230     source.Top	  = g_coord.Y + cLines;
5231     source.Right  = g_srScrollRegion.Right;
5232     source.Bottom = g_srScrollRegion.Bottom;
5233 
5234     fill.Char.AsciiChar = ' ';
5235     fill.Attributes = g_attrCurrent;
5236 
5237     ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
5238 
5239     /* Here we have to deal with a win32 console flake: If the scroll
5240      * region looks like abc and we scroll c to a and fill with d we get
5241      * cbd... if we scroll block c one line at a time to a, we get cdd...
5242      * vim expects cdd consistently... So we have to deal with that
5243      * here... (this also occurs scrolling the same way in the other
5244      * direction).  */
5245 
5246     nb = dest.Y + (source.Bottom - source.Top) + 1;
5247 
5248     if (nb < source.Top)
5249     {
5250 	COORD coord;
5251 
5252 	coord.X = 0;
5253 	coord.Y = nb;
5254 	clear_chars(coord, Columns * (source.Top - nb));
5255     }
5256 }
5257 
5258 
5259 /*
5260  * Set the cursor position
5261  */
5262     static void
5263 gotoxy(
5264     unsigned x,
5265     unsigned y)
5266 {
5267     if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows)
5268 	return;
5269 
5270     /* external cursor coords are 1-based; internal are 0-based */
5271     g_coord.X = x - 1;
5272     g_coord.Y = y - 1;
5273     SetConsoleCursorPosition(g_hConOut, g_coord);
5274 }
5275 
5276 
5277 /*
5278  * Set the current text attribute = (foreground | background)
5279  * See ../doc/os_win32.txt for the numbers.
5280  */
5281     static void
5282 textattr(WORD wAttr)
5283 {
5284     g_attrCurrent = wAttr & 0xff;
5285 
5286     SetConsoleTextAttribute(g_hConOut, wAttr);
5287 }
5288 
5289 
5290     static void
5291 textcolor(WORD wAttr)
5292 {
5293     g_attrCurrent = (g_attrCurrent & 0xf0) + (wAttr & 0x0f);
5294 
5295     SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
5296 }
5297 
5298 
5299     static void
5300 textbackground(WORD wAttr)
5301 {
5302     g_attrCurrent = (g_attrCurrent & 0x0f) + ((wAttr & 0x0f) << 4);
5303 
5304     SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
5305 }
5306 
5307 
5308 /*
5309  * restore the default text attribute (whatever we started with)
5310  */
5311     static void
5312 normvideo(void)
5313 {
5314     textattr(g_attrDefault);
5315 }
5316 
5317 
5318 static WORD g_attrPreStandout = 0;
5319 
5320 /*
5321  * Make the text standout, by brightening it
5322  */
5323     static void
5324 standout(void)
5325 {
5326     g_attrPreStandout = g_attrCurrent;
5327     textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
5328 }
5329 
5330 
5331 /*
5332  * Turn off standout mode
5333  */
5334     static void
5335 standend(void)
5336 {
5337     if (g_attrPreStandout)
5338     {
5339 	textattr(g_attrPreStandout);
5340 	g_attrPreStandout = 0;
5341     }
5342 }
5343 
5344 
5345 /*
5346  * Set normal fg/bg color, based on T_ME.  Called when t_me has been set.
5347  */
5348     void
5349 mch_set_normal_colors(void)
5350 {
5351     char_u	*p;
5352     int		n;
5353 
5354     cterm_normal_fg_color = (g_attrDefault & 0xf) + 1;
5355     cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1;
5356     if (T_ME[0] == ESC && T_ME[1] == '|')
5357     {
5358 	p = T_ME + 2;
5359 	n = getdigits(&p);
5360 	if (*p == 'm' && n > 0)
5361 	{
5362 	    cterm_normal_fg_color = (n & 0xf) + 1;
5363 	    cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
5364 	}
5365     }
5366 }
5367 
5368 
5369 /*
5370  * visual bell: flash the screen
5371  */
5372     static void
5373 visual_bell(void)
5374 {
5375     COORD   coordOrigin = {0, 0};
5376     WORD    attrFlash = ~g_attrCurrent & 0xff;
5377 
5378     DWORD   dwDummy;
5379     LPWORD  oldattrs = (LPWORD)alloc(Rows * Columns * sizeof(WORD));
5380 
5381     if (oldattrs == NULL)
5382 	return;
5383     ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
5384 			       coordOrigin, &dwDummy);
5385     FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns,
5386 			       coordOrigin, &dwDummy);
5387 
5388     Sleep(15);	    /* wait for 15 msec */
5389     WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
5390 				coordOrigin, &dwDummy);
5391     vim_free(oldattrs);
5392 }
5393 
5394 
5395 /*
5396  * Make the cursor visible or invisible
5397  */
5398     static void
5399 cursor_visible(BOOL fVisible)
5400 {
5401     s_cursor_visible = fVisible;
5402 #ifdef MCH_CURSOR_SHAPE
5403     mch_update_cursor();
5404 #endif
5405 }
5406 
5407 
5408 /*
5409  * write `cbToWrite' bytes in `pchBuf' to the screen
5410  * Returns the number of bytes actually written (at least one).
5411  */
5412     static DWORD
5413 write_chars(
5414     char_u *pchBuf,
5415     DWORD  cbToWrite)
5416 {
5417     COORD coord = g_coord;
5418     DWORD written;
5419 
5420 #ifdef FEAT_MBYTE
5421     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
5422     {
5423 	static WCHAR	*unicodebuf = NULL;
5424 	static int	unibuflen = 0;
5425 	int		length;
5426 	DWORD		n, cchwritten, cells;
5427 
5428 	length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite, 0, 0);
5429 	if (unicodebuf == NULL || length > unibuflen)
5430 	{
5431 	    vim_free(unicodebuf);
5432 	    unicodebuf = (WCHAR *)lalloc(length * sizeof(WCHAR), FALSE);
5433 	    unibuflen = length;
5434 	}
5435 	MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite,
5436 			    unicodebuf, unibuflen);
5437 
5438 	cells = mb_string2cells(pchBuf, cbToWrite);
5439 	FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cells,
5440 				    coord, &written);
5441 	/* When writing fails or didn't write a single character, pretend one
5442 	 * character was written, otherwise we get stuck. */
5443 	if (WriteConsoleOutputCharacterW(g_hConOut, unicodebuf, length,
5444 		    coord, &cchwritten) == 0
5445 		|| cchwritten == 0)
5446 	    cchwritten = 1;
5447 
5448 	if (cchwritten == length)
5449 	{
5450 	    written = cbToWrite;
5451 	    g_coord.X += (SHORT)cells;
5452 	}
5453 	else
5454 	{
5455 	    char_u *p = pchBuf;
5456 	    for (n = 0; n < cchwritten; n++)
5457 		mb_cptr_adv(p);
5458 	    written = p - pchBuf;
5459 	    g_coord.X += (SHORT)mb_string2cells(pchBuf, written);
5460 	}
5461     }
5462     else
5463 #endif
5464     {
5465 	FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cbToWrite,
5466 				    coord, &written);
5467 	/* When writing fails or didn't write a single character, pretend one
5468 	 * character was written, otherwise we get stuck. */
5469 	if (WriteConsoleOutputCharacter(g_hConOut, (LPCSTR)pchBuf, cbToWrite,
5470 		    coord, &written) == 0
5471 		|| written == 0)
5472 	    written = 1;
5473 
5474 	g_coord.X += (SHORT) written;
5475     }
5476 
5477     while (g_coord.X > g_srScrollRegion.Right)
5478     {
5479 	g_coord.X -= (SHORT) Columns;
5480 	if (g_coord.Y < g_srScrollRegion.Bottom)
5481 	    g_coord.Y++;
5482     }
5483 
5484     gotoxy(g_coord.X + 1, g_coord.Y + 1);
5485 
5486     return written;
5487 }
5488 
5489 
5490 /*
5491  * mch_write(): write the output buffer to the screen, translating ESC
5492  * sequences into calls to console output routines.
5493  */
5494     void
5495 mch_write(
5496     char_u  *s,
5497     int	    len)
5498 {
5499     s[len] = NUL;
5500 
5501     if (!term_console)
5502     {
5503 	write(1, s, (unsigned)len);
5504 	return;
5505     }
5506 
5507     /* translate ESC | sequences into faked bios calls */
5508     while (len--)
5509     {
5510 	/* optimization: use one single write_chars for runs of text,
5511 	 * rather than once per character  It ain't curses, but it helps. */
5512 	DWORD  prefix = (DWORD)strcspn(s, "\n\r\b\a\033");
5513 
5514 	if (p_wd)
5515 	{
5516 	    WaitForChar(p_wd);
5517 	    if (prefix != 0)
5518 		prefix = 1;
5519 	}
5520 
5521 	if (prefix != 0)
5522 	{
5523 	    DWORD nWritten;
5524 
5525 	    nWritten = write_chars(s, prefix);
5526 #ifdef MCH_WRITE_DUMP
5527 	    if (fdDump)
5528 	    {
5529 		fputc('>', fdDump);
5530 		fwrite(s, sizeof(char_u), nWritten, fdDump);
5531 		fputs("<\n", fdDump);
5532 	    }
5533 #endif
5534 	    len -= (nWritten - 1);
5535 	    s += nWritten;
5536 	}
5537 	else if (s[0] == '\n')
5538 	{
5539 	    /* \n, newline: go to the beginning of the next line or scroll */
5540 	    if (g_coord.Y == g_srScrollRegion.Bottom)
5541 	    {
5542 		scroll(1);
5543 		gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
5544 	    }
5545 	    else
5546 	    {
5547 		gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
5548 	    }
5549 #ifdef MCH_WRITE_DUMP
5550 	    if (fdDump)
5551 		fputs("\\n\n", fdDump);
5552 #endif
5553 	    s++;
5554 	}
5555 	else if (s[0] == '\r')
5556 	{
5557 	    /* \r, carriage return: go to beginning of line */
5558 	    gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
5559 #ifdef MCH_WRITE_DUMP
5560 	    if (fdDump)
5561 		fputs("\\r\n", fdDump);
5562 #endif
5563 	    s++;
5564 	}
5565 	else if (s[0] == '\b')
5566 	{
5567 	    /* \b, backspace: move cursor one position left */
5568 	    if (g_coord.X > g_srScrollRegion.Left)
5569 		g_coord.X--;
5570 	    else if (g_coord.Y > g_srScrollRegion.Top)
5571 	    {
5572 		g_coord.X = g_srScrollRegion.Right;
5573 		g_coord.Y--;
5574 	    }
5575 	    gotoxy(g_coord.X + 1, g_coord.Y + 1);
5576 #ifdef MCH_WRITE_DUMP
5577 	    if (fdDump)
5578 		fputs("\\b\n", fdDump);
5579 #endif
5580 	    s++;
5581 	}
5582 	else if (s[0] == '\a')
5583 	{
5584 	    /* \a, bell */
5585 	    MessageBeep(0xFFFFFFFF);
5586 #ifdef MCH_WRITE_DUMP
5587 	    if (fdDump)
5588 		fputs("\\a\n", fdDump);
5589 #endif
5590 	    s++;
5591 	}
5592 	else if (s[0] == ESC && len >= 3-1 && s[1] == '|')
5593 	{
5594 #ifdef MCH_WRITE_DUMP
5595 	    char_u  *old_s = s;
5596 #endif
5597 	    char_u  *p;
5598 	    int	    arg1 = 0, arg2 = 0;
5599 
5600 	    switch (s[2])
5601 	    {
5602 	    /* one or two numeric arguments, separated by ';' */
5603 
5604 	    case '0': case '1': case '2': case '3': case '4':
5605 	    case '5': case '6': case '7': case '8': case '9':
5606 		p = s + 2;
5607 		arg1 = getdigits(&p);	    /* no check for length! */
5608 		if (p > s + len)
5609 		    break;
5610 
5611 		if (*p == ';')
5612 		{
5613 		    ++p;
5614 		    arg2 = getdigits(&p);   /* no check for length! */
5615 		    if (p > s + len)
5616 			break;
5617 
5618 		    if (*p == 'H')
5619 			gotoxy(arg2, arg1);
5620 		    else if (*p == 'r')
5621 			set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
5622 		}
5623 		else if (*p == 'A')
5624 		{
5625 		    /* move cursor up arg1 lines in same column */
5626 		    gotoxy(g_coord.X + 1,
5627 			   max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
5628 		}
5629 		else if (*p == 'C')
5630 		{
5631 		    /* move cursor right arg1 columns in same line */
5632 		    gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
5633 			   g_coord.Y + 1);
5634 		}
5635 		else if (*p == 'H')
5636 		{
5637 		    gotoxy(1, arg1);
5638 		}
5639 		else if (*p == 'L')
5640 		{
5641 		    insert_lines(arg1);
5642 		}
5643 		else if (*p == 'm')
5644 		{
5645 		    if (arg1 == 0)
5646 			normvideo();
5647 		    else
5648 			textattr((WORD) arg1);
5649 		}
5650 		else if (*p == 'f')
5651 		{
5652 		    textcolor((WORD) arg1);
5653 		}
5654 		else if (*p == 'b')
5655 		{
5656 		    textbackground((WORD) arg1);
5657 		}
5658 		else if (*p == 'M')
5659 		{
5660 		    delete_lines(arg1);
5661 		}
5662 
5663 		len -= (int)(p - s);
5664 		s = p + 1;
5665 		break;
5666 
5667 
5668 	    /* Three-character escape sequences */
5669 
5670 	    case 'A':
5671 		/* move cursor up one line in same column */
5672 		gotoxy(g_coord.X + 1,
5673 		       max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
5674 		goto got3;
5675 
5676 	    case 'B':
5677 		visual_bell();
5678 		goto got3;
5679 
5680 	    case 'C':
5681 		/* move cursor right one column in same line */
5682 		gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
5683 		       g_coord.Y + 1);
5684 		goto got3;
5685 
5686 	    case 'E':
5687 		termcap_mode_end();
5688 		goto got3;
5689 
5690 	    case 'F':
5691 		standout();
5692 		goto got3;
5693 
5694 	    case 'f':
5695 		standend();
5696 		goto got3;
5697 
5698 	    case 'H':
5699 		gotoxy(1, 1);
5700 		goto got3;
5701 
5702 	    case 'j':
5703 		clear_to_end_of_display();
5704 		goto got3;
5705 
5706 	    case 'J':
5707 		clear_screen();
5708 		goto got3;
5709 
5710 	    case 'K':
5711 		clear_to_end_of_line();
5712 		goto got3;
5713 
5714 	    case 'L':
5715 		insert_lines(1);
5716 		goto got3;
5717 
5718 	    case 'M':
5719 		delete_lines(1);
5720 		goto got3;
5721 
5722 	    case 'S':
5723 		termcap_mode_start();
5724 		goto got3;
5725 
5726 	    case 'V':
5727 		cursor_visible(TRUE);
5728 		goto got3;
5729 
5730 	    case 'v':
5731 		cursor_visible(FALSE);
5732 		goto got3;
5733 
5734 	    got3:
5735 		s += 3;
5736 		len -= 2;
5737 	    }
5738 
5739 #ifdef MCH_WRITE_DUMP
5740 	    if (fdDump)
5741 	    {
5742 		fputs("ESC | ", fdDump);
5743 		fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
5744 		fputc('\n', fdDump);
5745 	    }
5746 #endif
5747 	}
5748 	else
5749 	{
5750 	    /* Write a single character */
5751 	    DWORD nWritten;
5752 
5753 	    nWritten = write_chars(s, 1);
5754 #ifdef MCH_WRITE_DUMP
5755 	    if (fdDump)
5756 	    {
5757 		fputc('>', fdDump);
5758 		fwrite(s, sizeof(char_u), nWritten, fdDump);
5759 		fputs("<\n", fdDump);
5760 	    }
5761 #endif
5762 
5763 	    len -= (nWritten - 1);
5764 	    s += nWritten;
5765 	}
5766     }
5767 
5768 #ifdef MCH_WRITE_DUMP
5769     if (fdDump)
5770 	fflush(fdDump);
5771 #endif
5772 }
5773 
5774 #endif /* FEAT_GUI_W32 */
5775 
5776 
5777 /*
5778  * Delay for half a second.
5779  */
5780 /*ARGSUSED*/
5781     void
5782 mch_delay(
5783     long    msec,
5784     int	    ignoreinput)
5785 {
5786 #ifdef FEAT_GUI_W32
5787     Sleep((int)msec);	    /* never wait for input */
5788 #else /* Console */
5789     if (ignoreinput)
5790 # ifdef FEAT_MZSCHEME
5791 	if (mzthreads_allowed() && p_mzq > 0 && msec > p_mzq)
5792 	{
5793 	    int towait = p_mzq;
5794 
5795 	    /* if msec is large enough, wait by portions in p_mzq */
5796 	    while (msec > 0)
5797 	    {
5798 		mzvim_check_threads();
5799 		if (msec < towait)
5800 		    towait = msec;
5801 		Sleep(towait);
5802 		msec -= towait;
5803 	    }
5804 	}
5805 	else
5806 # endif
5807 	    Sleep((int)msec);
5808     else
5809 	WaitForChar(msec);
5810 #endif
5811 }
5812 
5813 
5814 /*
5815  * this version of remove is not scared by a readonly (backup) file
5816  * Return 0 for success, -1 for failure.
5817  */
5818     int
5819 mch_remove(char_u *name)
5820 {
5821 #ifdef FEAT_MBYTE
5822     WCHAR	*wn = NULL;
5823     int		n;
5824 #endif
5825 
5826     win32_setattrs(name, FILE_ATTRIBUTE_NORMAL);
5827 
5828 #ifdef FEAT_MBYTE
5829     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
5830     {
5831 	wn = enc_to_utf16(name, NULL);
5832 	if (wn != NULL)
5833 	{
5834 	    n = DeleteFileW(wn) ? 0 : -1;
5835 	    vim_free(wn);
5836 	    if (n == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
5837 		return n;
5838 	    /* Retry with non-wide function (for Windows 98). */
5839 	}
5840     }
5841 #endif
5842     return DeleteFile(name) ? 0 : -1;
5843 }
5844 
5845 
5846 /*
5847  * check for an "interrupt signal": CTRL-break or CTRL-C
5848  */
5849     void
5850 mch_breakcheck(void)
5851 {
5852 #ifndef FEAT_GUI_W32	    /* never used */
5853     if (g_fCtrlCPressed || g_fCBrkPressed)
5854     {
5855 	g_fCtrlCPressed = g_fCBrkPressed = FALSE;
5856 	got_int = TRUE;
5857     }
5858 #endif
5859 }
5860 
5861 
5862 #ifdef FEAT_MBYTE
5863 /*
5864  * Same code as below, but with wide functions and no comments.
5865  * Return 0 for success, non-zero for failure.
5866  */
5867     int
5868 mch_wrename(WCHAR *wold, WCHAR *wnew)
5869 {
5870     WCHAR	*p;
5871     int		i;
5872     WCHAR	szTempFile[_MAX_PATH + 1];
5873     WCHAR	szNewPath[_MAX_PATH + 1];
5874     HANDLE	hf;
5875 
5876     if (!mch_windows95())
5877     {
5878 	p = wold;
5879 	for (i = 0; wold[i] != NUL; ++i)
5880 	    if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':')
5881 		    && wold[i + 1] != 0)
5882 		p = wold + i + 1;
5883 	if ((int)(wold + i - p) < 8 || p[6] != '~')
5884 	    return (MoveFileW(wold, wnew) == 0);
5885     }
5886 
5887     if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL)
5888 	return -1;
5889     *p = NUL;
5890 
5891     if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0)
5892 	return -2;
5893 
5894     if (!DeleteFileW(szTempFile))
5895 	return -3;
5896 
5897     if (!MoveFileW(wold, szTempFile))
5898 	return -4;
5899 
5900     if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW,
5901 		    FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
5902 	return -5;
5903     if (!CloseHandle(hf))
5904 	return -6;
5905 
5906     if (!MoveFileW(szTempFile, wnew))
5907     {
5908 	(void)MoveFileW(szTempFile, wold);
5909 	return -7;
5910     }
5911 
5912     DeleteFileW(szTempFile);
5913 
5914     if (!DeleteFileW(wold))
5915 	return -8;
5916 
5917     return 0;
5918 }
5919 #endif
5920 
5921 
5922 /*
5923  * mch_rename() works around a bug in rename (aka MoveFile) in
5924  * Windows 95: rename("foo.bar", "foo.bar~") will generate a
5925  * file whose short file name is "FOO.BAR" (its long file name will
5926  * be correct: "foo.bar~").  Because a file can be accessed by
5927  * either its SFN or its LFN, "foo.bar" has effectively been
5928  * renamed to "foo.bar", which is not at all what was wanted.  This
5929  * seems to happen only when renaming files with three-character
5930  * extensions by appending a suffix that does not include ".".
5931  * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
5932  *
5933  * There is another problem, which isn't really a bug but isn't right either:
5934  * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be
5935  * "abcdef~1.txt" again.  This has been reported on Windows NT 4.0 with
5936  * service pack 6.  Doesn't seem to happen on Windows 98.
5937  *
5938  * Like rename(), returns 0 upon success, non-zero upon failure.
5939  * Should probably set errno appropriately when errors occur.
5940  */
5941     int
5942 mch_rename(
5943     const char	*pszOldFile,
5944     const char	*pszNewFile)
5945 {
5946     char	szTempFile[_MAX_PATH+1];
5947     char	szNewPath[_MAX_PATH+1];
5948     char	*pszFilePart;
5949     HANDLE	hf;
5950 #ifdef FEAT_MBYTE
5951     WCHAR	*wold = NULL;
5952     WCHAR	*wnew = NULL;
5953     int		retval = -1;
5954 
5955     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
5956     {
5957 	wold = enc_to_utf16((char_u *)pszOldFile, NULL);
5958 	wnew = enc_to_utf16((char_u *)pszNewFile, NULL);
5959 	if (wold != NULL && wnew != NULL)
5960 	    retval = mch_wrename(wold, wnew);
5961 	vim_free(wold);
5962 	vim_free(wnew);
5963 	if (retval == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
5964 	    return retval;
5965 	/* Retry with non-wide function (for Windows 98). */
5966     }
5967 #endif
5968 
5969     /*
5970      * No need to play tricks if not running Windows 95, unless the file name
5971      * contains a "~" as the seventh character.
5972      */
5973     if (!mch_windows95())
5974     {
5975 	pszFilePart = (char *)gettail((char_u *)pszOldFile);
5976 	if (STRLEN(pszFilePart) < 8 || pszFilePart[6] != '~')
5977 	    return rename(pszOldFile, pszNewFile);
5978     }
5979 
5980     /* Get base path of new file name.  Undocumented feature: If pszNewFile is
5981      * a directory, no error is returned and pszFilePart will be NULL. */
5982     if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0
5983 	    || pszFilePart == NULL)
5984 	return -1;
5985     *pszFilePart = NUL;
5986 
5987     /* Get (and create) a unique temporary file name in directory of new file */
5988     if (GetTempFileName(szNewPath, "VIM", 0, szTempFile) == 0)
5989 	return -2;
5990 
5991     /* blow the temp file away */
5992     if (!DeleteFile(szTempFile))
5993 	return -3;
5994 
5995     /* rename old file to the temp file */
5996     if (!MoveFile(pszOldFile, szTempFile))
5997 	return -4;
5998 
5999     /* now create an empty file called pszOldFile; this prevents the operating
6000      * system using pszOldFile as an alias (SFN) if we're renaming within the
6001      * same directory.  For example, we're editing a file called
6002      * filename.asc.txt by its SFN, filena~1.txt.  If we rename filena~1.txt
6003      * to filena~1.txt~ (i.e., we're making a backup while writing it), the
6004      * SFN for filena~1.txt~ will be filena~1.txt, by default, which will
6005      * cause all sorts of problems later in buf_write().  So, we create an
6006      * empty file called filena~1.txt and the system will have to find some
6007      * other SFN for filena~1.txt~, such as filena~2.txt
6008      */
6009     if ((hf = CreateFile(pszOldFile, GENERIC_WRITE, 0, NULL, CREATE_NEW,
6010 		    FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
6011 	return -5;
6012     if (!CloseHandle(hf))
6013 	return -6;
6014 
6015     /* rename the temp file to the new file */
6016     if (!MoveFile(szTempFile, pszNewFile))
6017     {
6018 	/* Renaming failed.  Rename the file back to its old name, so that it
6019 	 * looks like nothing happened. */
6020 	(void)MoveFile(szTempFile, pszOldFile);
6021 
6022 	return -7;
6023     }
6024 
6025     /* Seems to be left around on Novell filesystems */
6026     DeleteFile(szTempFile);
6027 
6028     /* finally, remove the empty old file */
6029     if (!DeleteFile(pszOldFile))
6030 	return -8;
6031 
6032     return 0;	/* success */
6033 }
6034 
6035 /*
6036  * Get the default shell for the current hardware platform
6037  */
6038     char *
6039 default_shell(void)
6040 {
6041     char* psz = NULL;
6042 
6043     PlatformId();
6044 
6045     if (g_PlatformId == VER_PLATFORM_WIN32_NT)		/* Windows NT */
6046 	psz = "cmd.exe";
6047     else if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS) /* Windows 95 */
6048 	psz = "command.com";
6049 
6050     return psz;
6051 }
6052 
6053 /*
6054  * mch_access() extends access() to do more detailed check on network drives.
6055  * Returns 0 if file "n" has access rights according to "p", -1 otherwise.
6056  */
6057     int
6058 mch_access(char *n, int p)
6059 {
6060     HANDLE	hFile;
6061     DWORD	am;
6062     int		retval = -1;	    /* default: fail */
6063 #ifdef FEAT_MBYTE
6064     WCHAR	*wn = NULL;
6065 
6066     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6067 	wn = enc_to_utf16(n, NULL);
6068 #endif
6069 
6070     if (mch_isdir(n))
6071     {
6072 	char TempName[_MAX_PATH + 16] = "";
6073 #ifdef FEAT_MBYTE
6074 	WCHAR TempNameW[_MAX_PATH + 16] = L"";
6075 #endif
6076 
6077 	if (p & R_OK)
6078 	{
6079 	    /* Read check is performed by seeing if we can do a find file on
6080 	     * the directory for any file. */
6081 #ifdef FEAT_MBYTE
6082 	    if (wn != NULL)
6083 	    {
6084 		int		    i;
6085 		WIN32_FIND_DATAW    d;
6086 
6087 		for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i)
6088 		    TempNameW[i] = wn[i];
6089 		if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/')
6090 		    TempNameW[i++] = '\\';
6091 		TempNameW[i++] = '*';
6092 		TempNameW[i++] = 0;
6093 
6094 		hFile = FindFirstFileW(TempNameW, &d);
6095 		if (hFile == INVALID_HANDLE_VALUE)
6096 		{
6097 		    if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
6098 			goto getout;
6099 
6100 		    /* Retry with non-wide function (for Windows 98). */
6101 		    vim_free(wn);
6102 		    wn = NULL;
6103 		}
6104 		else
6105 		    (void)FindClose(hFile);
6106 	    }
6107 	    if (wn == NULL)
6108 #endif
6109 	    {
6110 		char		    *pch;
6111 		WIN32_FIND_DATA	    d;
6112 
6113 		vim_strncpy(TempName, n, _MAX_PATH);
6114 		pch = TempName + STRLEN(TempName) - 1;
6115 		if (*pch != '\\' && *pch != '/')
6116 		    *++pch = '\\';
6117 		*++pch = '*';
6118 		*++pch = NUL;
6119 
6120 		hFile = FindFirstFile(TempName, &d);
6121 		if (hFile == INVALID_HANDLE_VALUE)
6122 		    goto getout;
6123 		(void)FindClose(hFile);
6124 	    }
6125 	}
6126 
6127 	if (p & W_OK)
6128 	{
6129 	    /* Trying to create a temporary file in the directory should catch
6130 	     * directories on read-only network shares.  However, in
6131 	     * directories whose ACL allows writes but denies deletes will end
6132 	     * up keeping the temporary file :-(. */
6133 #ifdef FEAT_MBYTE
6134 	    if (wn != NULL)
6135 	    {
6136 		if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW))
6137 		{
6138 		    if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
6139 			goto getout;
6140 
6141 		    /* Retry with non-wide function (for Windows 98). */
6142 		    vim_free(wn);
6143 		    wn = NULL;
6144 		}
6145 		else
6146 		    DeleteFileW(TempNameW);
6147 	    }
6148 	    if (wn == NULL)
6149 #endif
6150 	    {
6151 		if (!GetTempFileName(n, "VIM", 0, TempName))
6152 		    goto getout;
6153 		mch_remove((char_u *)TempName);
6154 	    }
6155 	}
6156     }
6157     else
6158     {
6159 	/* Trying to open the file for the required access does ACL, read-only
6160 	 * network share, and file attribute checks.  */
6161 	am = ((p & W_OK) ? GENERIC_WRITE : 0)
6162 		| ((p & R_OK) ? GENERIC_READ : 0);
6163 #ifdef FEAT_MBYTE
6164 	if (wn != NULL)
6165 	{
6166 	    hFile = CreateFileW(wn, am, 0, NULL, OPEN_EXISTING, 0, NULL);
6167 	    if (hFile == INVALID_HANDLE_VALUE
6168 			      && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
6169 	    {
6170 		/* Retry with non-wide function (for Windows 98). */
6171 		vim_free(wn);
6172 		wn = NULL;
6173 	    }
6174 	}
6175 	if (wn == NULL)
6176 #endif
6177 	    hFile = CreateFile(n, am, 0, NULL, OPEN_EXISTING, 0, NULL);
6178 	if (hFile == INVALID_HANDLE_VALUE)
6179 	    goto getout;
6180 	CloseHandle(hFile);
6181     }
6182 
6183     retval = 0;	    /* success */
6184 getout:
6185 #ifdef FEAT_MBYTE
6186     vim_free(wn);
6187 #endif
6188     return retval;
6189 }
6190 
6191 #if defined(FEAT_MBYTE) || defined(PROTO)
6192 /*
6193  * Version of open() that may use UTF-16 file name.
6194  */
6195     int
6196 mch_open(char *name, int flags, int mode)
6197 {
6198     /* _wopen() does not work with Borland C 5.5: creates a read-only file. */
6199 # ifndef __BORLANDC__
6200     WCHAR	*wn;
6201     int		f;
6202 
6203     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6204     {
6205 	wn = enc_to_utf16(name, NULL);
6206 	if (wn != NULL)
6207 	{
6208 	    f = _wopen(wn, flags, mode);
6209 	    vim_free(wn);
6210 	    if (f >= 0 || g_PlatformId == VER_PLATFORM_WIN32_NT)
6211 		return f;
6212 	    /* Retry with non-wide function (for Windows 98). Can't use
6213 	     * GetLastError() here and it's unclear what errno gets set to if
6214 	     * the _wopen() fails for missing wide functions. */
6215 	}
6216     }
6217 # endif
6218 
6219     /* open() can open a file which name is longer than _MAX_PATH bytes
6220      * and shorter than _MAX_PATH characters successfully, but sometimes it
6221      * causes unexpected error in another part. We make it an error explicitly
6222      * here. */
6223     if (strlen(name) >= _MAX_PATH)
6224 	return -1;
6225 
6226     return open(name, flags, mode);
6227 }
6228 
6229 /*
6230  * Version of fopen() that may use UTF-16 file name.
6231  */
6232     FILE *
6233 mch_fopen(char *name, char *mode)
6234 {
6235     WCHAR	*wn, *wm;
6236     FILE	*f = NULL;
6237 
6238     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
6239 # ifdef __BORLANDC__
6240 	    /* Wide functions of Borland C 5.5 do not work on Windows 98. */
6241 	    && g_PlatformId == VER_PLATFORM_WIN32_NT
6242 # endif
6243        )
6244     {
6245 # if defined(DEBUG) && _MSC_VER >= 1400
6246 	/* Work around an annoying assertion in the Microsoft debug CRT
6247 	 * when mode's text/binary setting doesn't match _get_fmode(). */
6248 	char newMode = mode[strlen(mode) - 1];
6249 	int oldMode = 0;
6250 
6251 	_get_fmode(&oldMode);
6252 	if (newMode == 't')
6253 	    _set_fmode(_O_TEXT);
6254 	else if (newMode == 'b')
6255 	    _set_fmode(_O_BINARY);
6256 # endif
6257 	wn = enc_to_utf16(name, NULL);
6258 	wm = enc_to_utf16(mode, NULL);
6259 	if (wn != NULL && wm != NULL)
6260 	    f = _wfopen(wn, wm);
6261 	vim_free(wn);
6262 	vim_free(wm);
6263 
6264 # if defined(DEBUG) && _MSC_VER >= 1400
6265 	_set_fmode(oldMode);
6266 # endif
6267 
6268 	if (f != NULL || g_PlatformId == VER_PLATFORM_WIN32_NT)
6269 	    return f;
6270 	/* Retry with non-wide function (for Windows 98). Can't use
6271 	 * GetLastError() here and it's unclear what errno gets set to if
6272 	 * the _wfopen() fails for missing wide functions. */
6273     }
6274 
6275     /* fopen() can open a file which name is longer than _MAX_PATH bytes
6276      * and shorter than _MAX_PATH characters successfully, but sometimes it
6277      * causes unexpected error in another part. We make it an error explicitly
6278      * here. */
6279     if (strlen(name) >= _MAX_PATH)
6280 	return NULL;
6281 
6282     return fopen(name, mode);
6283 }
6284 #endif
6285 
6286 #ifdef FEAT_MBYTE
6287 /*
6288  * SUB STREAM (aka info stream) handling:
6289  *
6290  * NTFS can have sub streams for each file.  Normal contents of file is
6291  * stored in the main stream, and extra contents (author information and
6292  * title and so on) can be stored in sub stream.  After Windows 2000, user
6293  * can access and store those informations in sub streams via explorer's
6294  * property menuitem in right click menu.  Those informations in sub streams
6295  * were lost when copying only the main stream.  So we have to copy sub
6296  * streams.
6297  *
6298  * Incomplete explanation:
6299  *	http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp
6300  * More useful info and an example:
6301  *	http://www.sysinternals.com/ntw2k/source/misc.shtml#streams
6302  */
6303 
6304 /*
6305  * Copy info stream data "substream".  Read from the file with BackupRead(sh)
6306  * and write to stream "substream" of file "to".
6307  * Errors are ignored.
6308  */
6309     static void
6310 copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len)
6311 {
6312     HANDLE  hTo;
6313     WCHAR   *to_name;
6314 
6315     to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR));
6316     wcscpy(to_name, to);
6317     wcscat(to_name, substream);
6318 
6319     hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
6320 						 FILE_ATTRIBUTE_NORMAL, NULL);
6321     if (hTo != INVALID_HANDLE_VALUE)
6322     {
6323 	long	done;
6324 	DWORD	todo;
6325 	DWORD	readcnt, written;
6326 	char	buf[4096];
6327 
6328 	/* Copy block of bytes at a time.  Abort when something goes wrong. */
6329 	for (done = 0; done < len; done += written)
6330 	{
6331 	    /* (size_t) cast for Borland C 5.5 */
6332 	    todo = (DWORD)((size_t)(len - done) > sizeof(buf) ? sizeof(buf)
6333 						       : (size_t)(len - done));
6334 	    if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt,
6335 						       FALSE, FALSE, context)
6336 		    || readcnt != todo
6337 		    || !WriteFile(hTo, buf, todo, &written, NULL)
6338 		    || written != todo)
6339 		break;
6340 	}
6341 	CloseHandle(hTo);
6342     }
6343 
6344     free(to_name);
6345 }
6346 
6347 /*
6348  * Copy info streams from file "from" to file "to".
6349  */
6350     static void
6351 copy_infostreams(char_u *from, char_u *to)
6352 {
6353     WCHAR		*fromw;
6354     WCHAR		*tow;
6355     HANDLE		sh;
6356     WIN32_STREAM_ID	sid;
6357     int			headersize;
6358     WCHAR		streamname[_MAX_PATH];
6359     DWORD		readcount;
6360     void		*context = NULL;
6361     DWORD		lo, hi;
6362     int			len;
6363 
6364     /* Convert the file names to wide characters. */
6365     fromw = enc_to_utf16(from, NULL);
6366     tow = enc_to_utf16(to, NULL);
6367     if (fromw != NULL && tow != NULL)
6368     {
6369 	/* Open the file for reading. */
6370 	sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL,
6371 			     OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
6372 	if (sh != INVALID_HANDLE_VALUE)
6373 	{
6374 	    /* Use BackupRead() to find the info streams.  Repeat until we
6375 	     * have done them all.*/
6376 	    for (;;)
6377 	    {
6378 		/* Get the header to find the length of the stream name.  If
6379 		 * the "readcount" is zero we have done all info streams. */
6380 		ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
6381 		headersize = (int)((char *)&sid.cStreamName - (char *)&sid.dwStreamId);
6382 		if (!BackupRead(sh, (LPBYTE)&sid, headersize,
6383 					   &readcount, FALSE, FALSE, &context)
6384 			|| readcount == 0)
6385 		    break;
6386 
6387 		/* We only deal with streams that have a name.  The normal
6388 		 * file data appears to be without a name, even though docs
6389 		 * suggest it is called "::$DATA". */
6390 		if (sid.dwStreamNameSize > 0)
6391 		{
6392 		    /* Read the stream name. */
6393 		    if (!BackupRead(sh, (LPBYTE)streamname,
6394 							 sid.dwStreamNameSize,
6395 					  &readcount, FALSE, FALSE, &context))
6396 			break;
6397 
6398 		    /* Copy an info stream with a name ":anything:$DATA".
6399 		     * Skip "::$DATA", it has no stream name (examples suggest
6400 		     * it might be used for the normal file contents).
6401 		     * Note that BackupRead() counts bytes, but the name is in
6402 		     * wide characters. */
6403 		    len = readcount / sizeof(WCHAR);
6404 		    streamname[len] = 0;
6405 		    if (len > 7 && wcsicmp(streamname + len - 6,
6406 							      L":$DATA") == 0)
6407 		    {
6408 			streamname[len - 6] = 0;
6409 			copy_substream(sh, &context, tow, streamname,
6410 						    (long)sid.Size.u.LowPart);
6411 		    }
6412 		}
6413 
6414 		/* Advance to the next stream.  We might try seeking too far,
6415 		 * but BackupSeek() doesn't skip over stream borders, thus
6416 		 * that's OK. */
6417 		(void)BackupSeek(sh, sid.Size.u.LowPart, sid.Size.u.HighPart,
6418 							  &lo, &hi, &context);
6419 	    }
6420 
6421 	    /* Clear the context. */
6422 	    (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context);
6423 
6424 	    CloseHandle(sh);
6425 	}
6426     }
6427     vim_free(fromw);
6428     vim_free(tow);
6429 }
6430 #endif
6431 
6432 /*
6433  * Copy file attributes from file "from" to file "to".
6434  * For Windows NT and later we copy info streams.
6435  * Always returns zero, errors are ignored.
6436  */
6437     int
6438 mch_copy_file_attribute(char_u *from, char_u *to)
6439 {
6440 #ifdef FEAT_MBYTE
6441     /* File streams only work on Windows NT and later. */
6442     PlatformId();
6443     if (g_PlatformId == VER_PLATFORM_WIN32_NT)
6444 	copy_infostreams(from, to);
6445 #endif
6446     return 0;
6447 }
6448 
6449 #if defined(MYRESETSTKOFLW) || defined(PROTO)
6450 /*
6451  * Recreate a destroyed stack guard page in win32.
6452  * Written by Benjamin Peterson.
6453  */
6454 
6455 /* These magic numbers are from the MS header files */
6456 #define MIN_STACK_WIN9X 17
6457 #define MIN_STACK_WINNT 2
6458 
6459 /*
6460  * This function does the same thing as _resetstkoflw(), which is only
6461  * available in DevStudio .net and later.
6462  * Returns 0 for failure, 1 for success.
6463  */
6464     int
6465 myresetstkoflw(void)
6466 {
6467     BYTE	*pStackPtr;
6468     BYTE	*pGuardPage;
6469     BYTE	*pStackBase;
6470     BYTE	*pLowestPossiblePage;
6471     MEMORY_BASIC_INFORMATION mbi;
6472     SYSTEM_INFO si;
6473     DWORD	nPageSize;
6474     DWORD	dummy;
6475 
6476     /* This code will not work on win32s. */
6477     PlatformId();
6478     if (g_PlatformId == VER_PLATFORM_WIN32s)
6479 	return 0;
6480 
6481     /* We need to know the system page size. */
6482     GetSystemInfo(&si);
6483     nPageSize = si.dwPageSize;
6484 
6485     /* ...and the current stack pointer */
6486     pStackPtr = (BYTE*)_alloca(1);
6487 
6488     /* ...and the base of the stack. */
6489     if (VirtualQuery(pStackPtr, &mbi, sizeof mbi) == 0)
6490 	return 0;
6491     pStackBase = (BYTE*)mbi.AllocationBase;
6492 
6493     /* ...and the page thats min_stack_req pages away from stack base; this is
6494      * the lowest page we could use. */
6495     pLowestPossiblePage = pStackBase + ((g_PlatformId == VER_PLATFORM_WIN32_NT)
6496 			     ? MIN_STACK_WINNT : MIN_STACK_WIN9X) * nPageSize;
6497 
6498     /* On Win95, we want the next page down from the end of the stack. */
6499     if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS)
6500     {
6501 	/* Find the page that's only 1 page down from the page that the stack
6502 	 * ptr is in. */
6503 	pGuardPage = (BYTE*)((DWORD)nPageSize * (((DWORD)pStackPtr
6504 						    / (DWORD)nPageSize) - 1));
6505 	if (pGuardPage < pLowestPossiblePage)
6506 	    return 0;
6507 
6508 	/* Apply the noaccess attribute to the page -- there's no guard
6509 	 * attribute in win95-type OSes. */
6510 	if (!VirtualProtect(pGuardPage, nPageSize, PAGE_NOACCESS, &dummy))
6511 	    return 0;
6512     }
6513     else
6514     {
6515 	/* On NT, however, we want the first committed page in the stack Start
6516 	 * at the stack base and move forward through memory until we find a
6517 	 * committed block. */
6518 	BYTE *pBlock = pStackBase;
6519 
6520 	for (;;)
6521 	{
6522 	    if (VirtualQuery(pBlock, &mbi, sizeof mbi) == 0)
6523 		return 0;
6524 
6525 	    pBlock += mbi.RegionSize;
6526 
6527 	    if (mbi.State & MEM_COMMIT)
6528 		break;
6529 	}
6530 
6531 	/* mbi now describes the first committed block in the stack. */
6532 	if (mbi.Protect & PAGE_GUARD)
6533 	    return 1;
6534 
6535 	/* decide where the guard page should start */
6536 	if ((long_u)(mbi.BaseAddress) < (long_u)pLowestPossiblePage)
6537 	    pGuardPage = pLowestPossiblePage;
6538 	else
6539 	    pGuardPage = (BYTE*)mbi.BaseAddress;
6540 
6541 	/* allocate the guard page */
6542 	if (!VirtualAlloc(pGuardPage, nPageSize, MEM_COMMIT, PAGE_READWRITE))
6543 	    return 0;
6544 
6545 	/* apply the guard attribute to the page */
6546 	if (!VirtualProtect(pGuardPage, nPageSize, PAGE_READWRITE | PAGE_GUARD,
6547 								      &dummy))
6548 	    return 0;
6549     }
6550 
6551     return 1;
6552 }
6553 #endif
6554 
6555 
6556 #if defined(FEAT_MBYTE) || defined(PROTO)
6557 /*
6558  * The command line arguments in UCS2
6559  */
6560 static int	nArgsW = 0;
6561 static LPWSTR	*ArglistW = NULL;
6562 static int	global_argc = 0;
6563 static char	**global_argv;
6564 
6565 static int	used_file_argc = 0;	/* last argument in global_argv[] used
6566 					   for the argument list. */
6567 static int	*used_file_indexes = NULL; /* indexes in global_argv[] for
6568 					      command line arguments added to
6569 					      the argument list */
6570 static int	used_file_count = 0;	/* nr of entries in used_file_indexes */
6571 static int	used_file_literal = FALSE;  /* take file names literally */
6572 static int	used_file_full_path = FALSE;  /* file name was full path */
6573 static int	used_file_diff_mode = FALSE;  /* file name was with diff mode */
6574 static int	used_alist_count = 0;
6575 
6576 
6577 /*
6578  * Get the command line arguments.  Unicode version.
6579  * Returns argc.  Zero when something fails.
6580  */
6581     int
6582 get_cmd_argsW(char ***argvp)
6583 {
6584     char	**argv = NULL;
6585     int		argc = 0;
6586     int		i;
6587 
6588     free_cmd_argsW();
6589     ArglistW = CommandLineToArgvW(GetCommandLineW(), &nArgsW);
6590     if (ArglistW != NULL)
6591     {
6592 	argv = malloc((nArgsW + 1) * sizeof(char *));
6593 	if (argv != NULL)
6594 	{
6595 	    argc = nArgsW;
6596 	    argv[argc] = NULL;
6597 	    for (i = 0; i < argc; ++i)
6598 	    {
6599 		int	len;
6600 
6601 		/* Convert each Unicode argument to the current codepage. */
6602 		WideCharToMultiByte_alloc(GetACP(), 0,
6603 				ArglistW[i], (int)wcslen(ArglistW[i]) + 1,
6604 				(LPSTR *)&argv[i], &len, 0, 0);
6605 		if (argv[i] == NULL)
6606 		{
6607 		    /* Out of memory, clear everything. */
6608 		    while (i > 0)
6609 			free(argv[--i]);
6610 		    free(argv);
6611 		    argv = NULL;
6612 		    argc = 0;
6613 		}
6614 	    }
6615 	}
6616     }
6617 
6618     global_argc = argc;
6619     global_argv = argv;
6620     if (argc > 0)
6621     {
6622 	if (used_file_indexes != NULL)
6623 	    free(used_file_indexes);
6624 	used_file_indexes = malloc(argc * sizeof(int));
6625     }
6626 
6627     if (argvp != NULL)
6628 	*argvp = argv;
6629     return argc;
6630 }
6631 
6632     void
6633 free_cmd_argsW(void)
6634 {
6635     if (ArglistW != NULL)
6636     {
6637 	GlobalFree(ArglistW);
6638 	ArglistW = NULL;
6639     }
6640 }
6641 
6642 /*
6643  * Remember "name" is an argument that was added to the argument list.
6644  * This avoids that we have to re-parse the argument list when fix_arg_enc()
6645  * is called.
6646  */
6647     void
6648 used_file_arg(char *name, int literal, int full_path, int diff_mode)
6649 {
6650     int		i;
6651 
6652     if (used_file_indexes == NULL)
6653 	return;
6654     for (i = used_file_argc + 1; i < global_argc; ++i)
6655 	if (STRCMP(global_argv[i], name) == 0)
6656 	{
6657 	    used_file_argc = i;
6658 	    used_file_indexes[used_file_count++] = i;
6659 	    break;
6660 	}
6661     used_file_literal = literal;
6662     used_file_full_path = full_path;
6663     used_file_diff_mode = diff_mode;
6664 }
6665 
6666 /*
6667  * Remember the length of the argument list as it was.  If it changes then we
6668  * leave it alone when 'encoding' is set.
6669  */
6670     void
6671 set_alist_count(void)
6672 {
6673     used_alist_count = GARGCOUNT;
6674 }
6675 
6676 /*
6677  * Fix the encoding of the command line arguments.  Invoked when 'encoding'
6678  * has been changed while starting up.  Use the UCS-2 command line arguments
6679  * and convert them to 'encoding'.
6680  */
6681     void
6682 fix_arg_enc(void)
6683 {
6684     int		i;
6685     int		idx;
6686     char_u	*str;
6687     int		*fnum_list;
6688 
6689     /* Safety checks:
6690      * - if argument count differs between the wide and non-wide argument
6691      *   list, something must be wrong.
6692      * - the file name arguments must have been located.
6693      * - the length of the argument list wasn't changed by the user.
6694      */
6695     if (global_argc != nArgsW
6696 	    || ArglistW == NULL
6697 	    || used_file_indexes == NULL
6698 	    || used_file_count == 0
6699 	    || used_alist_count != GARGCOUNT)
6700 	return;
6701 
6702     /* Remember the buffer numbers for the arguments. */
6703     fnum_list = (int *)alloc((int)sizeof(int) * GARGCOUNT);
6704     if (fnum_list == NULL)
6705 	return;		/* out of memory */
6706     for (i = 0; i < GARGCOUNT; ++i)
6707 	fnum_list[i] = GARGLIST[i].ae_fnum;
6708 
6709     /* Clear the argument list.  Make room for the new arguments. */
6710     alist_clear(&global_alist);
6711     if (ga_grow(&global_alist.al_ga, used_file_count) == FAIL)
6712 	return;		/* out of memory */
6713 
6714     for (i = 0; i < used_file_count; ++i)
6715     {
6716 	idx = used_file_indexes[i];
6717 	str = utf16_to_enc(ArglistW[idx], NULL);
6718 	if (str != NULL)
6719 	{
6720 #ifdef FEAT_DIFF
6721 	    /* When using diff mode may need to concatenate file name to
6722 	     * directory name.  Just like it's done in main(). */
6723 	    if (used_file_diff_mode && mch_isdir(str) && GARGCOUNT > 0
6724 				      && !mch_isdir(alist_name(&GARGLIST[0])))
6725 	    {
6726 		char_u	    *r;
6727 
6728 		r = concat_fnames(str, gettail(alist_name(&GARGLIST[0])), TRUE);
6729 		if (r != NULL)
6730 		{
6731 		    vim_free(str);
6732 		    str = r;
6733 		}
6734 	    }
6735 #endif
6736 	    /* Re-use the old buffer by renaming it.  When not using literal
6737 	     * names it's done by alist_expand() below. */
6738 	    if (used_file_literal)
6739 		buf_set_name(fnum_list[i], str);
6740 
6741 	    alist_add(&global_alist, str, used_file_literal ? 2 : 0);
6742 	}
6743     }
6744 
6745     if (!used_file_literal)
6746     {
6747 	/* Now expand wildcards in the arguments. */
6748 	/* Temporarily add '(' and ')' to 'isfname'.  These are valid
6749 	 * filename characters but are excluded from 'isfname' to make
6750 	 * "gf" work on a file name in parenthesis (e.g.: see vim.h). */
6751 	do_cmdline_cmd((char_u *)":let SaVe_ISF = &isf|set isf+=(,)");
6752 	alist_expand(fnum_list, used_alist_count);
6753 	do_cmdline_cmd((char_u *)":let &isf = SaVe_ISF|unlet SaVe_ISF");
6754     }
6755 
6756     /* If wildcard expansion failed, we are editing the first file of the
6757      * arglist and there is no file name: Edit the first argument now. */
6758     if (curwin->w_arg_idx == 0 && curbuf->b_fname == NULL)
6759     {
6760 	do_cmdline_cmd((char_u *)":rewind");
6761 	if (GARGCOUNT == 1 && used_file_full_path)
6762 	    (void)vim_chdirfile(alist_name(&GARGLIST[0]));
6763     }
6764 
6765     set_alist_count();
6766 }
6767 #endif
6768