blob: 0c252ac8d17516f9b173075ab439a131b23d3773 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* 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 <george@reilly.org> wrote most of this.
20 * Roger Knobbe <rogerk@wonderware.com> did the initial port of Vim 3.0.
21 */
22
Bram Moolenaar071d4272004-06-13 20:20:40 +000023#include "vim.h"
24
Bram Moolenaar325b7a22004-07-05 15:58:32 +000025#ifdef FEAT_MZSCHEME
26# include "if_mzsch.h"
27#endif
28
Bram Moolenaar071d4272004-06-13 20:20:40 +000029#include <sys/types.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000030#include <signal.h>
31#include <limits.h>
Bram Moolenaar82881492012-11-20 16:53:39 +010032
33/* cproto fails on missing include files */
34#ifndef PROTO
35# include <process.h>
36#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000037
38#undef chdir
39#ifdef __GNUC__
40# ifndef __MINGW32__
41# include <dirent.h>
42# endif
43#else
44# include <direct.h>
45#endif
46
Bram Moolenaar82881492012-11-20 16:53:39 +010047#ifndef PROTO
48# if defined(FEAT_TITLE) && !defined(FEAT_GUI_W32)
49# include <shellapi.h>
50# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000051#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
85FILE* 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
Bram Moolenaar071d4272004-06-13 20:20:40 +000094typedef char * LPCSTR;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +000095typedef char * LPWSTR;
Bram Moolenaar071d4272004-06-13 20:20:40 +000096typedef int ACCESS_MASK;
97typedef int BOOL;
98typedef int COLORREF;
99typedef int CONSOLE_CURSOR_INFO;
100typedef int COORD;
101typedef int DWORD;
102typedef int HANDLE;
Bram Moolenaaref269542016-01-19 13:22:12 +0100103typedef int LPHANDLE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000104typedef int HDC;
105typedef int HFONT;
106typedef int HICON;
107typedef int HINSTANCE;
108typedef int HWND;
109typedef int INPUT_RECORD;
110typedef int KEY_EVENT_RECORD;
111typedef int LOGFONT;
112typedef int LPBOOL;
113typedef int LPCTSTR;
114typedef int LPDWORD;
115typedef int LPSTR;
116typedef int LPTSTR;
117typedef int LPVOID;
118typedef int MOUSE_EVENT_RECORD;
119typedef int PACL;
120typedef int PDWORD;
121typedef int PHANDLE;
122typedef int PRINTDLG;
123typedef int PSECURITY_DESCRIPTOR;
124typedef int PSID;
125typedef int SECURITY_INFORMATION;
126typedef int SHORT;
127typedef int SMALL_RECT;
128typedef int TEXTMETRIC;
129typedef int TOKEN_INFORMATION_CLASS;
130typedef int TRUSTEE;
131typedef int WORD;
132typedef int WCHAR;
133typedef void VOID;
Bram Moolenaar82881492012-11-20 16:53:39 +0100134typedef int BY_HANDLE_FILE_INFORMATION;
Bram Moolenaar32ac8cd2013-07-03 18:49:17 +0200135typedef int SE_OBJECT_TYPE;
136typedef int PSNSECINFO;
137typedef int PSNSECINFOW;
Bram Moolenaarb8e0bdb2014-11-12 16:10:48 +0100138typedef int STARTUPINFO;
139typedef int PROCESS_INFORMATION;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000140#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
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100150/* WINAPI BOOL WINAPI GetConsoleKeyboardLayoutNameA(LPSTR); */
151#ifndef WINAPI
152# define WINAPI __stdcall
Bram Moolenaar071d4272004-06-13 20:20:40 +0000153#endif
154#if defined(__BORLANDC__)
155typedef BOOL (__stdcall *PFNGCKLN)(LPSTR);
156#else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100157typedef BOOL (WINAPI *PFNGCKLN)(LPSTR);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000158#endif
Bram Moolenaard6f676d2005-06-01 21:51:55 +0000159static PFNGCKLN s_pfnGetConsoleKeyboardLayoutName = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160#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
Bram Moolenaar82881492012-11-20 16:53:39 +0100167#ifndef PROTO
168
Bram Moolenaar84a05ac2013-05-06 04:24:17 +0200169/* Enable common dialogs input unicode from IME if possible. */
Bram Moolenaar8c85fa32011-08-10 17:08:03 +0200170#ifdef FEAT_MBYTE
Bram Moolenaaraf62ff32013-03-19 14:48:29 +0100171LRESULT (WINAPI *pDispatchMessage)(CONST MSG *) = DispatchMessage;
Bram Moolenaar8c85fa32011-08-10 17:08:03 +0200172BOOL (WINAPI *pGetMessage)(LPMSG, HWND, UINT, UINT) = GetMessage;
173BOOL (WINAPI *pIsDialogMessage)(HWND, LPMSG) = IsDialogMessage;
174BOOL (WINAPI *pPeekMessage)(LPMSG, HWND, UINT, UINT, UINT) = PeekMessage;
175#endif
176
Bram Moolenaar82881492012-11-20 16:53:39 +0100177#endif /* PROTO */
178
Bram Moolenaar071d4272004-06-13 20:20:40 +0000179#ifndef FEAT_GUI_W32
180/* Win32 Console handles for input and output */
181static HANDLE g_hConIn = INVALID_HANDLE_VALUE;
182static HANDLE g_hConOut = INVALID_HANDLE_VALUE;
183
184/* Win32 Screen buffer,coordinate,console I/O information */
185static SMALL_RECT g_srScrollRegion;
186static COORD g_coord; /* 0-based, but external coords are 1-based */
187
188/* The attribute of the screen when the editor was started */
189static WORD g_attrDefault = 7; /* lightgray text on black background */
190static WORD g_attrCurrent;
191
192static int g_fCBrkPressed = FALSE; /* set by ctrl-break interrupt */
193static int g_fCtrlCPressed = FALSE; /* set when ctrl-C or ctrl-break detected */
194static int g_fForceExit = FALSE; /* set when forcefully exiting */
195
196static void termcap_mode_start(void);
197static void termcap_mode_end(void);
198static void clear_chars(COORD coord, DWORD n);
199static void clear_screen(void);
200static void clear_to_end_of_display(void);
201static void clear_to_end_of_line(void);
202static void scroll(unsigned cLines);
203static void set_scroll_region(unsigned left, unsigned top,
204 unsigned right, unsigned bottom);
205static void insert_lines(unsigned cLines);
206static void delete_lines(unsigned cLines);
207static void gotoxy(unsigned x, unsigned y);
208static void normvideo(void);
209static void textattr(WORD wAttr);
210static void textcolor(WORD wAttr);
211static void textbackground(WORD wAttr);
212static void standout(void);
213static void standend(void);
214static void visual_bell(void);
215static void cursor_visible(BOOL fVisible);
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200216static DWORD write_chars(char_u *pchBuf, DWORD cbToWrite);
217static WCHAR tgetch(int *pmodifiers, WCHAR *pch2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000218static void create_conin(void);
219static int s_cursor_visible = TRUE;
220static int did_create_conin = FALSE;
221#else
222static int s_dont_use_vimrun = TRUE;
223static int need_vimrun_warning = FALSE;
224static char *vimrun_path = "vimrun ";
225#endif
226
Bram Moolenaar12b559e2013-06-12 22:41:37 +0200227static int win32_getattrs(char_u *name);
228static int win32_setattrs(char_u *name, int attrs);
229static int win32_set_archive(char_u *name);
230
Bram Moolenaar071d4272004-06-13 20:20:40 +0000231#ifndef FEAT_GUI_W32
232static int suppress_winsize = 1; /* don't fiddle with console */
233#endif
234
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200235static char_u *exe_path = NULL;
236
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100237static BOOL win8_or_later = FALSE;
238
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100239/*
240 * Version of ReadConsoleInput() that works with IME.
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100241 * Works around problems on Windows 8.
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100242 */
243 static BOOL
244read_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100245 HANDLE hInput,
246 INPUT_RECORD *lpBuffer,
247 DWORD nLength,
248 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100249{
250 enum
251 {
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100252 IRSIZE = 10
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100253 };
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100254 static INPUT_RECORD s_irCache[IRSIZE];
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100255 static DWORD s_dwIndex = 0;
256 static DWORD s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100257 DWORD dwEvents;
Bram Moolenaardd415a62014-02-05 14:02:27 +0100258 int head;
259 int tail;
260 int i;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100261
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200262 if (nLength == -2)
263 return (s_dwMax > 0) ? TRUE : FALSE;
264
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100265 if (!win8_or_later)
266 {
267 if (nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200268 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
269 return ReadConsoleInputW(hInput, lpBuffer, 1, &dwEvents);
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100270 }
271
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100272 if (s_dwMax == 0)
273 {
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100274 if (nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200275 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
276 if (!ReadConsoleInputW(hInput, s_irCache, IRSIZE, &dwEvents))
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100277 return FALSE;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100278 s_dwIndex = 0;
279 s_dwMax = dwEvents;
280 if (dwEvents == 0)
281 {
282 *lpEvents = 0;
283 return TRUE;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100284 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100285
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 }
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100306 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100307
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100308 *lpBuffer = s_irCache[s_dwIndex];
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200309 if (!(nLength == -1 || nLength == -2) && ++s_dwIndex >= s_dwMax)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100310 s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100311 *lpEvents = 1;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100312 return TRUE;
313}
314
315/*
316 * Version of PeekConsoleInput() that works with IME.
317 */
318 static BOOL
319peek_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100320 HANDLE hInput,
321 INPUT_RECORD *lpBuffer,
322 DWORD nLength,
323 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100324{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100325 return read_console_input(hInput, lpBuffer, -1, lpEvents);
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100326}
327
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200328 static DWORD
329msg_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
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100342#ifndef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200343 static DWORD
344wait_for_single_object(
345 HANDLE hHandle,
346 DWORD dwMilliseconds)
347{
348 if (read_console_input(NULL, NULL, -2, NULL))
349 return WAIT_OBJECT_0;
350 return WaitForSingleObject(hHandle, dwMilliseconds);
351}
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100352#endif
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200353
Bram Moolenaar071d4272004-06-13 20:20:40 +0000354 static void
355get_exe_name(void)
356{
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100357 /* Maximum length of $PATH is more than MAXPATHL. 8191 is often mentioned
358 * as the maximum length that works (plus a NUL byte). */
359#define MAX_ENV_PATH_LEN 8192
360 char temp[MAX_ENV_PATH_LEN];
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200361 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000362
363 if (exe_name == NULL)
364 {
365 /* store the name of the executable, may be used for $VIM */
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100366 GetModuleFileName(NULL, temp, MAX_ENV_PATH_LEN - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000367 if (*temp != NUL)
368 exe_name = FullName_save((char_u *)temp, FALSE);
369 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000370
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200371 if (exe_path == NULL && exe_name != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000372 {
Bram Moolenaar6b5ef062010-10-27 12:18:00 +0200373 exe_path = vim_strnsave(exe_name,
374 (int)(gettail_sep(exe_name) - exe_name));
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200375 if (exe_path != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000376 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200377 /* Append our starting directory to $PATH, so that when doing
378 * "!xxd" it's found in our starting directory. Needed because
379 * SearchPath() also looks there. */
380 p = mch_getenv("PATH");
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100381 if (p == NULL
382 || STRLEN(p) + STRLEN(exe_path) + 2 < MAX_ENV_PATH_LEN)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200383 {
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100384 if (p == NULL || *p == NUL)
385 temp[0] = NUL;
386 else
387 {
388 STRCPY(temp, p);
389 STRCAT(temp, ";");
390 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200391 STRCAT(temp, exe_path);
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100392 vim_setenv((char_u *)"PATH", (char_u *)temp);
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200393 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000394 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000395 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000396}
397
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200398/*
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100399 * Unescape characters in "p" that appear in "escaped".
400 */
401 static void
402unescape_shellxquote(char_u *p, char_u *escaped)
403{
Bram Moolenaar4336cdf2012-02-29 13:58:47 +0100404 int l = (int)STRLEN(p);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100405 int n;
406
407 while (*p != NUL)
408 {
409 if (*p == '^' && vim_strchr(escaped, p[1]) != NULL)
410 mch_memmove(p, p + 1, l--);
411#ifdef FEAT_MBYTE
412 n = (*mb_ptr2len)(p);
413#else
414 n = 1;
415#endif
416 p += n;
417 l -= n;
418 }
419}
420
421/*
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200422 * Load library "name".
423 */
424 HINSTANCE
425vimLoadLib(char *name)
426{
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200427 HINSTANCE dll = NULL;
428 char old_dir[MAXPATHL];
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200429
Bram Moolenaarfaca8402012-10-21 02:37:10 +0200430 /* NOTE: Do not use mch_dirname() and mch_chdir() here, they may call
431 * vimLoadLib() recursively, which causes a stack overflow. */
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200432 if (exe_path == NULL)
433 get_exe_name();
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200434 if (exe_path != NULL)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200435 {
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200436#ifdef FEAT_MBYTE
437 WCHAR old_dirw[MAXPATHL];
438
439 if (GetCurrentDirectoryW(MAXPATHL, old_dirw) != 0)
440 {
441 /* Change directory to where the executable is, both to make
442 * sure we find a .dll there and to avoid looking for a .dll
443 * in the current directory. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100444 SetCurrentDirectory((LPCSTR)exe_path);
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200445 dll = LoadLibrary(name);
446 SetCurrentDirectoryW(old_dirw);
447 return dll;
448 }
449 /* Retry with non-wide function (for Windows 98). */
450 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
451#endif
452 if (GetCurrentDirectory(MAXPATHL, old_dir) != 0)
453 {
454 /* Change directory to where the executable is, both to make
455 * sure we find a .dll there and to avoid looking for a .dll
456 * in the current directory. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100457 SetCurrentDirectory((LPCSTR)exe_path);
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200458 dll = LoadLibrary(name);
459 SetCurrentDirectory(old_dir);
460 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200461 }
462 return dll;
463}
464
Bram Moolenaar071d4272004-06-13 20:20:40 +0000465#if defined(DYNAMIC_GETTEXT) || defined(PROTO)
466# ifndef GETTEXT_DLL
467# define GETTEXT_DLL "libintl.dll"
Bram Moolenaar286eacd2016-01-16 18:05:50 +0100468# define GETTEXT_DLL_ALT "libintl-8.dll"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000469# endif
Bram Moolenaarf6a2b082012-06-29 13:14:03 +0200470/* Dummy functions */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000471static char *null_libintl_gettext(const char *);
472static char *null_libintl_textdomain(const char *);
473static char *null_libintl_bindtextdomain(const char *, const char *);
474static char *null_libintl_bind_textdomain_codeset(const char *, const char *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200476static HINSTANCE hLibintlDLL = NULL;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000477char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
478char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
479char *(*dyn_libintl_bindtextdomain)(const char *, const char *)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000480 = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000481char *(*dyn_libintl_bind_textdomain_codeset)(const char *, const char *)
482 = null_libintl_bind_textdomain_codeset;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000483
484 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100485dyn_libintl_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000486{
487 int i;
488 static struct
489 {
490 char *name;
491 FARPROC *ptr;
492 } libintl_entry[] =
493 {
494 {"gettext", (FARPROC*)&dyn_libintl_gettext},
495 {"textdomain", (FARPROC*)&dyn_libintl_textdomain},
496 {"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
497 {NULL, NULL}
498 };
499
500 /* No need to initialize twice. */
501 if (hLibintlDLL)
502 return 1;
503 /* Load gettext library (libintl.dll) */
Bram Moolenaar923e43b2016-01-28 15:07:38 +0100504 hLibintlDLL = vimLoadLib(GETTEXT_DLL);
Bram Moolenaar938ee832016-01-24 15:36:03 +0100505#ifdef GETTEXT_DLL_ALT
Bram Moolenaar286eacd2016-01-16 18:05:50 +0100506 if (!hLibintlDLL)
507 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT);
Bram Moolenaar938ee832016-01-24 15:36:03 +0100508#endif
509 if (!hLibintlDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000510 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200511 if (p_verbose > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000512 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200513 verbose_enter();
514 EMSG2(_(e_loadlib), GETTEXT_DLL);
515 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000516 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200517 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000518 }
519 for (i = 0; libintl_entry[i].name != NULL
520 && libintl_entry[i].ptr != NULL; ++i)
521 {
522 if ((*libintl_entry[i].ptr = (FARPROC)GetProcAddress(hLibintlDLL,
523 libintl_entry[i].name)) == NULL)
524 {
525 dyn_libintl_end();
526 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000527 {
528 verbose_enter();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529 EMSG2(_(e_loadfunc), libintl_entry[i].name);
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000530 verbose_leave();
531 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000532 return 0;
533 }
534 }
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000535
536 /* The bind_textdomain_codeset() function is optional. */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000537 dyn_libintl_bind_textdomain_codeset = (void *)GetProcAddress(hLibintlDLL,
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000538 "bind_textdomain_codeset");
539 if (dyn_libintl_bind_textdomain_codeset == NULL)
540 dyn_libintl_bind_textdomain_codeset =
541 null_libintl_bind_textdomain_codeset;
542
Bram Moolenaar071d4272004-06-13 20:20:40 +0000543 return 1;
544}
545
546 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100547dyn_libintl_end(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000548{
549 if (hLibintlDLL)
550 FreeLibrary(hLibintlDLL);
551 hLibintlDLL = NULL;
552 dyn_libintl_gettext = null_libintl_gettext;
553 dyn_libintl_textdomain = null_libintl_textdomain;
554 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000555 dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000556}
557
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000558/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000559 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000560null_libintl_gettext(const char *msgid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000561{
562 return (char*)msgid;
563}
564
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000565/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000567null_libintl_bindtextdomain(const char *domainname, const char *dirname)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000568{
569 return NULL;
570}
571
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000572/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000573 static char *
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000574null_libintl_bind_textdomain_codeset(const char *domainname,
575 const char *codeset)
576{
577 return NULL;
578}
579
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000580/*ARGSUSED*/
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000581 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000582null_libintl_textdomain(const char *domainname)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000583{
584 return NULL;
585}
586
587#endif /* DYNAMIC_GETTEXT */
588
589/* This symbol is not defined in older versions of the SDK or Visual C++ */
590
591#ifndef VER_PLATFORM_WIN32_WINDOWS
592# define VER_PLATFORM_WIN32_WINDOWS 1
593#endif
594
595DWORD g_PlatformId;
596
597#ifdef HAVE_ACL
Bram Moolenaar82881492012-11-20 16:53:39 +0100598# ifndef PROTO
599# include <aclapi.h>
600# endif
Bram Moolenaar27515922013-06-29 15:36:26 +0200601# ifndef PROTECTED_DACL_SECURITY_INFORMATION
602# define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000L
603# endif
Bram Moolenaar82881492012-11-20 16:53:39 +0100604
Bram Moolenaar071d4272004-06-13 20:20:40 +0000605/*
606 * These are needed to dynamically load the ADVAPI DLL, which is not
607 * implemented under Windows 95 (and causes VIM to crash)
608 */
Bram Moolenaar39efa892013-06-29 15:40:04 +0200609typedef DWORD (WINAPI *PSNSECINFO) (LPSTR, SE_OBJECT_TYPE,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000610 SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
Bram Moolenaar39efa892013-06-29 15:40:04 +0200611typedef DWORD (WINAPI *PGNSECINFO) (LPSTR, SE_OBJECT_TYPE,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000612 SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *,
613 PSECURITY_DESCRIPTOR *);
Bram Moolenaar27515922013-06-29 15:36:26 +0200614# ifdef FEAT_MBYTE
Bram Moolenaar39efa892013-06-29 15:40:04 +0200615typedef DWORD (WINAPI *PSNSECINFOW) (LPWSTR, SE_OBJECT_TYPE,
Bram Moolenaar27515922013-06-29 15:36:26 +0200616 SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
Bram Moolenaar39efa892013-06-29 15:40:04 +0200617typedef DWORD (WINAPI *PGNSECINFOW) (LPWSTR, SE_OBJECT_TYPE,
Bram Moolenaar27515922013-06-29 15:36:26 +0200618 SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *,
619 PSECURITY_DESCRIPTOR *);
620# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621
622static HANDLE advapi_lib = NULL; /* Handle for ADVAPI library */
623static PSNSECINFO pSetNamedSecurityInfo;
624static PGNSECINFO pGetNamedSecurityInfo;
Bram Moolenaar27515922013-06-29 15:36:26 +0200625# ifdef FEAT_MBYTE
626static PSNSECINFOW pSetNamedSecurityInfoW;
627static PGNSECINFOW pGetNamedSecurityInfoW;
628# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000629#endif
630
Bram Moolenaar4b9669f2011-07-07 16:20:52 +0200631typedef BOOL (WINAPI *PSETHANDLEINFORMATION)(HANDLE, DWORD, DWORD);
632
633static BOOL allowPiping = FALSE;
634static PSETHANDLEINFORMATION pSetHandleInformation;
635
Bram Moolenaar27515922013-06-29 15:36:26 +0200636#ifdef HAVE_ACL
637/*
638 * Enables or disables the specified privilege.
639 */
640 static BOOL
641win32_enable_privilege(LPTSTR lpszPrivilege, BOOL bEnable)
642{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100643 BOOL bResult;
644 LUID luid;
645 HANDLE hToken;
646 TOKEN_PRIVILEGES tokenPrivileges;
Bram Moolenaar27515922013-06-29 15:36:26 +0200647
648 if (!OpenProcessToken(GetCurrentProcess(),
649 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
650 return FALSE;
651
652 if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
653 {
654 CloseHandle(hToken);
655 return FALSE;
656 }
657
Bram Moolenaar45500912014-07-09 20:51:07 +0200658 tokenPrivileges.PrivilegeCount = 1;
Bram Moolenaar27515922013-06-29 15:36:26 +0200659 tokenPrivileges.Privileges[0].Luid = luid;
660 tokenPrivileges.Privileges[0].Attributes = bEnable ?
661 SE_PRIVILEGE_ENABLED : 0;
662
663 bResult = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges,
664 sizeof(TOKEN_PRIVILEGES), NULL, NULL);
665
666 CloseHandle(hToken);
667
668 return bResult && GetLastError() == ERROR_SUCCESS;
669}
670#endif
671
Bram Moolenaar071d4272004-06-13 20:20:40 +0000672/*
673 * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
674 * VER_PLATFORM_WIN32_WINDOWS (Win95).
675 */
676 void
677PlatformId(void)
678{
679 static int done = FALSE;
680
681 if (!done)
682 {
683 OSVERSIONINFO ovi;
684
685 ovi.dwOSVersionInfoSize = sizeof(ovi);
686 GetVersionEx(&ovi);
687
688 g_PlatformId = ovi.dwPlatformId;
689
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100690 if ((ovi.dwMajorVersion == 6 && ovi.dwMinorVersion >= 2)
691 || ovi.dwMajorVersion > 6)
692 win8_or_later = TRUE;
693
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694#ifdef HAVE_ACL
695 /*
696 * Load the ADVAPI runtime if we are on anything
697 * other than Windows 95
698 */
699 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
700 {
701 /*
702 * do this load. Problems: Doesn't unload at end of run (this is
703 * theoretically okay, since Windows should unload it when VIM
704 * terminates). Should we be using the 'mch_libcall' routines?
705 * Seems like a lot of overhead to load/unload ADVAPI32.DLL each
706 * time we verify security...
707 */
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200708 advapi_lib = vimLoadLib("ADVAPI32.DLL");
Bram Moolenaar071d4272004-06-13 20:20:40 +0000709 if (advapi_lib != NULL)
710 {
711 pSetNamedSecurityInfo = (PSNSECINFO)GetProcAddress(advapi_lib,
712 "SetNamedSecurityInfoA");
713 pGetNamedSecurityInfo = (PGNSECINFO)GetProcAddress(advapi_lib,
714 "GetNamedSecurityInfoA");
Bram Moolenaar27515922013-06-29 15:36:26 +0200715# ifdef FEAT_MBYTE
716 pSetNamedSecurityInfoW = (PSNSECINFOW)GetProcAddress(advapi_lib,
717 "SetNamedSecurityInfoW");
718 pGetNamedSecurityInfoW = (PGNSECINFOW)GetProcAddress(advapi_lib,
719 "GetNamedSecurityInfoW");
720# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000721 if (pSetNamedSecurityInfo == NULL
Bram Moolenaar27515922013-06-29 15:36:26 +0200722 || pGetNamedSecurityInfo == NULL
723# ifdef FEAT_MBYTE
724 || pSetNamedSecurityInfoW == NULL
725 || pGetNamedSecurityInfoW == NULL
726# endif
727 )
Bram Moolenaar071d4272004-06-13 20:20:40 +0000728 {
729 /* If we can't get the function addresses, set advapi_lib
730 * to NULL so that we don't use them. */
731 FreeLibrary(advapi_lib);
732 advapi_lib = NULL;
733 }
Bram Moolenaar27515922013-06-29 15:36:26 +0200734 /* Enable privilege for getting or setting SACLs. */
735 win32_enable_privilege(SE_SECURITY_NAME, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000736 }
737 }
738#endif
Bram Moolenaar4b9669f2011-07-07 16:20:52 +0200739 /*
740 * If we are on windows NT, try to load the pipe functions, only
741 * available from Win2K.
742 */
743 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
744 {
745 HANDLE kernel32 = GetModuleHandle("kernel32");
746 pSetHandleInformation = (PSETHANDLEINFORMATION)GetProcAddress(
747 kernel32, "SetHandleInformation");
748
749 allowPiping = pSetHandleInformation != NULL;
750 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000751 done = TRUE;
752 }
753}
754
755/*
756 * Return TRUE when running on Windows 95 (or 98 or ME).
757 * Only to be used after mch_init().
758 */
759 int
760mch_windows95(void)
761{
762 return g_PlatformId == VER_PLATFORM_WIN32_WINDOWS;
763}
764
765#ifdef FEAT_GUI_W32
766/*
767 * Used to work around the "can't do synchronous spawn"
768 * problem on Win32s, without resorting to Universal Thunk.
769 */
770static int old_num_windows;
771static int num_windows;
772
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000773/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000774 static BOOL CALLBACK
775win32ssynch_cb(HWND hwnd, LPARAM lparam)
776{
777 num_windows++;
778 return TRUE;
779}
780#endif
781
782#ifndef FEAT_GUI_W32
783
784#define SHIFT (SHIFT_PRESSED)
785#define CTRL (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
786#define ALT (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
787#define ALT_GR (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)
788
789
790/* When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
791 * We map function keys to their ANSI terminal equivalents, as produced
792 * by ANSI.SYS, for compatibility with the MS-DOS version of Vim. Any
793 * ANSI key with a value >= '\300' is nonstandard, but provided anyway
794 * so that the user can have access to all SHIFT-, CTRL-, and ALT-
795 * combinations of function/arrow/etc keys.
796 */
797
Bram Moolenaard6f676d2005-06-01 21:51:55 +0000798static const struct
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799{
800 WORD wVirtKey;
801 BOOL fAnsiKey;
802 int chAlone;
803 int chShift;
804 int chCtrl;
805 int chAlt;
806} VirtKeyMap[] =
807{
808
809/* Key ANSI alone shift ctrl alt */
810 { VK_ESCAPE,FALSE, ESC, ESC, ESC, ESC, },
811
812 { VK_F1, TRUE, ';', 'T', '^', 'h', },
813 { VK_F2, TRUE, '<', 'U', '_', 'i', },
814 { VK_F3, TRUE, '=', 'V', '`', 'j', },
815 { VK_F4, TRUE, '>', 'W', 'a', 'k', },
816 { VK_F5, TRUE, '?', 'X', 'b', 'l', },
817 { VK_F6, TRUE, '@', 'Y', 'c', 'm', },
818 { VK_F7, TRUE, 'A', 'Z', 'd', 'n', },
819 { VK_F8, TRUE, 'B', '[', 'e', 'o', },
820 { VK_F9, TRUE, 'C', '\\', 'f', 'p', },
821 { VK_F10, TRUE, 'D', ']', 'g', 'q', },
822 { VK_F11, TRUE, '\205', '\207', '\211', '\213', },
823 { VK_F12, TRUE, '\206', '\210', '\212', '\214', },
824
825 { VK_HOME, TRUE, 'G', '\302', 'w', '\303', },
826 { VK_UP, TRUE, 'H', '\304', '\305', '\306', },
827 { VK_PRIOR, TRUE, 'I', '\307', '\204', '\310', }, /*PgUp*/
828 { VK_LEFT, TRUE, 'K', '\311', 's', '\312', },
829 { VK_RIGHT, TRUE, 'M', '\313', 't', '\314', },
830 { VK_END, TRUE, 'O', '\315', 'u', '\316', },
831 { VK_DOWN, TRUE, 'P', '\317', '\320', '\321', },
832 { VK_NEXT, TRUE, 'Q', '\322', 'v', '\323', }, /*PgDn*/
833 { VK_INSERT,TRUE, 'R', '\324', '\325', '\326', },
834 { VK_DELETE,TRUE, 'S', '\327', '\330', '\331', },
835
836 { VK_SNAPSHOT,TRUE, 0, 0, 0, 'r', }, /*PrtScrn*/
837
838#if 0
839 /* Most people don't have F13-F20, but what the hell... */
840 { VK_F13, TRUE, '\332', '\333', '\334', '\335', },
841 { VK_F14, TRUE, '\336', '\337', '\340', '\341', },
842 { VK_F15, TRUE, '\342', '\343', '\344', '\345', },
843 { VK_F16, TRUE, '\346', '\347', '\350', '\351', },
844 { VK_F17, TRUE, '\352', '\353', '\354', '\355', },
845 { VK_F18, TRUE, '\356', '\357', '\360', '\361', },
846 { VK_F19, TRUE, '\362', '\363', '\364', '\365', },
847 { VK_F20, TRUE, '\366', '\367', '\370', '\371', },
848#endif
849 { VK_ADD, TRUE, 'N', 'N', 'N', 'N', }, /* keyp '+' */
850 { VK_SUBTRACT, TRUE,'J', 'J', 'J', 'J', }, /* keyp '-' */
851 /* { VK_DIVIDE, TRUE,'N', 'N', 'N', 'N', }, keyp '/' */
852 { VK_MULTIPLY, TRUE,'7', '7', '7', '7', }, /* keyp '*' */
853
854 { VK_NUMPAD0,TRUE, '\332', '\333', '\334', '\335', },
855 { VK_NUMPAD1,TRUE, '\336', '\337', '\340', '\341', },
856 { VK_NUMPAD2,TRUE, '\342', '\343', '\344', '\345', },
857 { VK_NUMPAD3,TRUE, '\346', '\347', '\350', '\351', },
858 { VK_NUMPAD4,TRUE, '\352', '\353', '\354', '\355', },
859 { VK_NUMPAD5,TRUE, '\356', '\357', '\360', '\361', },
860 { VK_NUMPAD6,TRUE, '\362', '\363', '\364', '\365', },
861 { VK_NUMPAD7,TRUE, '\366', '\367', '\370', '\371', },
862 { VK_NUMPAD8,TRUE, '\372', '\373', '\374', '\375', },
863 /* Sorry, out of number space! <negri>*/
864 { VK_NUMPAD9,TRUE, '\376', '\377', '\377', '\367', },
865
866};
867
868
869#ifdef _MSC_VER
870// The ToAscii bug destroys several registers. Need to turn off optimization
871// or the GetConsoleKeyboardLayoutName hack will fail in non-debug versions
Bram Moolenaar7b5f8322006-03-23 22:47:08 +0000872# pragma warning(push)
873# pragma warning(disable: 4748)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000874# pragma optimize("", off)
875#endif
876
877#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200878# define UChar UnicodeChar
Bram Moolenaar071d4272004-06-13 20:20:40 +0000879#else
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200880# define UChar uChar.UnicodeChar
Bram Moolenaar071d4272004-06-13 20:20:40 +0000881#endif
882
883/* The return code indicates key code size. */
884 static int
885#ifdef __BORLANDC__
886 __stdcall
887#endif
888win32_kbd_patch_key(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000889 KEY_EVENT_RECORD *pker)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000890{
891 UINT uMods = pker->dwControlKeyState;
892 static int s_iIsDead = 0;
893 static WORD awAnsiCode[2];
894 static BYTE abKeystate[256];
895
896
897 if (s_iIsDead == 2)
898 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200899 pker->UChar = (WCHAR) awAnsiCode[1];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000900 s_iIsDead = 0;
901 return 1;
902 }
903
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200904 if (pker->UChar != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000905 return 1;
906
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200907 vim_memset(abKeystate, 0, sizeof (abKeystate));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000908
909 // Should only be non-NULL on NT 4.0
910 if (s_pfnGetConsoleKeyboardLayoutName != NULL)
911 {
912 CHAR szKLID[KL_NAMELENGTH];
913
914 if ((*s_pfnGetConsoleKeyboardLayoutName)(szKLID))
915 (void)LoadKeyboardLayout(szKLID, KLF_ACTIVATE);
916 }
917
918 /* Clear any pending dead keys */
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200919 ToUnicode(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000920
921 if (uMods & SHIFT_PRESSED)
922 abKeystate[VK_SHIFT] = 0x80;
923 if (uMods & CAPSLOCK_ON)
924 abKeystate[VK_CAPITAL] = 1;
925
926 if ((uMods & ALT_GR) == ALT_GR)
927 {
928 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
929 abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
930 }
931
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200932 s_iIsDead = ToUnicode(pker->wVirtualKeyCode, pker->wVirtualScanCode,
933 abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000934
935 if (s_iIsDead > 0)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200936 pker->UChar = (WCHAR) awAnsiCode[0];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937
938 return s_iIsDead;
939}
940
941#ifdef _MSC_VER
942/* MUST switch optimization on again here, otherwise a call to
943 * decode_key_event() may crash (e.g. when hitting caps-lock) */
944# pragma optimize("", on)
Bram Moolenaar7b5f8322006-03-23 22:47:08 +0000945# pragma warning(pop)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946
947# if (_MSC_VER < 1100)
948/* MUST turn off global optimisation for this next function, or
949 * pressing ctrl-minus in insert mode crashes Vim when built with
950 * VC4.1. -- negri. */
951# pragma optimize("g", off)
952# endif
953#endif
954
955static BOOL g_fJustGotFocus = FALSE;
956
957/*
958 * Decode a KEY_EVENT into one or two keystrokes
959 */
960 static BOOL
961decode_key_event(
962 KEY_EVENT_RECORD *pker,
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200963 WCHAR *pch,
964 WCHAR *pch2,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000965 int *pmodifiers,
966 BOOL fDoPost)
967{
968 int i;
969 const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
970
971 *pch = *pch2 = NUL;
972 g_fJustGotFocus = FALSE;
973
974 /* ignore key up events */
975 if (!pker->bKeyDown)
976 return FALSE;
977
978 /* ignore some keystrokes */
979 switch (pker->wVirtualKeyCode)
980 {
981 /* modifiers */
982 case VK_SHIFT:
983 case VK_CONTROL:
984 case VK_MENU: /* Alt key */
985 return FALSE;
986
987 default:
988 break;
989 }
990
991 /* special cases */
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200992 if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->UChar == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000993 {
994 /* Ctrl-6 is Ctrl-^ */
995 if (pker->wVirtualKeyCode == '6')
996 {
997 *pch = Ctrl_HAT;
998 return TRUE;
999 }
1000 /* Ctrl-2 is Ctrl-@ */
1001 else if (pker->wVirtualKeyCode == '2')
1002 {
1003 *pch = NUL;
1004 return TRUE;
1005 }
1006 /* Ctrl-- is Ctrl-_ */
1007 else if (pker->wVirtualKeyCode == 0xBD)
1008 {
1009 *pch = Ctrl__;
1010 return TRUE;
1011 }
1012 }
1013
1014 /* Shift-TAB */
1015 if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED))
1016 {
1017 *pch = K_NUL;
1018 *pch2 = '\017';
1019 return TRUE;
1020 }
1021
1022 for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]); --i >= 0; )
1023 {
1024 if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
1025 {
1026 if (nModifs == 0)
1027 *pch = VirtKeyMap[i].chAlone;
1028 else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
1029 *pch = VirtKeyMap[i].chShift;
1030 else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
1031 *pch = VirtKeyMap[i].chCtrl;
1032 else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
1033 *pch = VirtKeyMap[i].chAlt;
1034
1035 if (*pch != 0)
1036 {
1037 if (VirtKeyMap[i].fAnsiKey)
1038 {
1039 *pch2 = *pch;
1040 *pch = K_NUL;
1041 }
1042
1043 return TRUE;
1044 }
1045 }
1046 }
1047
1048 i = win32_kbd_patch_key(pker);
1049
1050 if (i < 0)
1051 *pch = NUL;
1052 else
1053 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001054 *pch = (i > 0) ? pker->UChar : NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055
1056 if (pmodifiers != NULL)
1057 {
1058 /* Pass on the ALT key as a modifier, but only when not combined
1059 * with CTRL (which is ALTGR). */
1060 if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0)
1061 *pmodifiers |= MOD_MASK_ALT;
1062
1063 /* Pass on SHIFT only for special keys, because we don't know when
1064 * it's already included with the character. */
1065 if ((nModifs & SHIFT) != 0 && *pch <= 0x20)
1066 *pmodifiers |= MOD_MASK_SHIFT;
1067
1068 /* Pass on CTRL only for non-special keys, because we don't know
1069 * when it's already included with the character. And not when
1070 * combined with ALT (which is ALTGR). */
1071 if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0
1072 && *pch >= 0x20 && *pch < 0x80)
1073 *pmodifiers |= MOD_MASK_CTRL;
1074 }
1075 }
1076
1077 return (*pch != NUL);
1078}
1079
1080#ifdef _MSC_VER
1081# pragma optimize("", on)
1082#endif
1083
1084#endif /* FEAT_GUI_W32 */
1085
1086
1087#ifdef FEAT_MOUSE
1088
1089/*
1090 * For the GUI the mouse handling is in gui_w32.c.
1091 */
1092# ifdef FEAT_GUI_W32
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00001093/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00001094 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001095mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001096{
1097}
1098# else
1099static int g_fMouseAvail = FALSE; /* mouse present */
1100static int g_fMouseActive = FALSE; /* mouse enabled */
1101static int g_nMouseClick = -1; /* mouse status */
1102static int g_xMouse; /* mouse x coordinate */
1103static int g_yMouse; /* mouse y coordinate */
1104
1105/*
1106 * Enable or disable mouse input
1107 */
1108 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001109mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001110{
1111 DWORD cmodein;
1112
1113 if (!g_fMouseAvail)
1114 return;
1115
1116 g_fMouseActive = on;
1117 GetConsoleMode(g_hConIn, &cmodein);
1118
1119 if (g_fMouseActive)
1120 cmodein |= ENABLE_MOUSE_INPUT;
1121 else
1122 cmodein &= ~ENABLE_MOUSE_INPUT;
1123
1124 SetConsoleMode(g_hConIn, cmodein);
1125}
1126
Bram Moolenaar4d919d72016-02-05 22:36:41 +01001127#ifdef FEAT_CHANNEL
1128 static int
1129handle_channel_event(void)
1130{
1131 int ret;
1132 fd_set rfds;
1133 int maxfd;
1134
1135 FD_ZERO(&rfds);
1136 maxfd = channel_select_setup(-1, &rfds);
1137 if (maxfd >= 0)
1138 {
1139 struct timeval tv;
1140
1141 tv.tv_sec = 0;
1142 tv.tv_usec = 0;
1143 ret = select(maxfd + 1, &rfds, NULL, NULL, &tv);
1144 if (ret > 0 && channel_select_check(ret, &rfds) > 0)
1145 return TRUE;
1146 }
1147 return FALSE;
1148}
1149#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001150
1151/*
1152 * Decode a MOUSE_EVENT. If it's a valid event, return MOUSE_LEFT,
1153 * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
1154 * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
1155 * or a MOUSE_LEFT, _MIDDLE, or _RIGHT. We encode the button type,
1156 * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
1157 * and we return the mouse position in g_xMouse and g_yMouse.
1158 *
1159 * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
1160 * MOUSE_DRAGs and one MOUSE_RELEASE. MOUSE_RELEASE will be followed only
1161 * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
1162 *
1163 * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
1164 * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
1165 *
1166 * Windows will send us MOUSE_MOVED notifications whenever the mouse
1167 * moves, even if it stays within the same character cell. We ignore
1168 * all MOUSE_MOVED messages if the position hasn't really changed, and
1169 * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
1170 * we're only interested in MOUSE_DRAG).
1171 *
1172 * All of this is complicated by the code that fakes MOUSE_MIDDLE on
1173 * 2-button mouses by pressing the left & right buttons simultaneously.
1174 * In practice, it's almost impossible to click both at the same time,
1175 * so we need to delay a little. Also, we tend not to get MOUSE_RELEASE
1176 * in such cases, if the user is clicking quickly.
1177 */
1178 static BOOL
1179decode_mouse_event(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001180 MOUSE_EVENT_RECORD *pmer)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001181{
1182 static int s_nOldButton = -1;
1183 static int s_nOldMouseClick = -1;
1184 static int s_xOldMouse = -1;
1185 static int s_yOldMouse = -1;
1186 static linenr_T s_old_topline = 0;
1187#ifdef FEAT_DIFF
1188 static int s_old_topfill = 0;
1189#endif
1190 static int s_cClicks = 1;
1191 static BOOL s_fReleased = TRUE;
1192 static DWORD s_dwLastClickTime = 0;
1193 static BOOL s_fNextIsMiddle = FALSE;
1194
1195 static DWORD cButtons = 0; /* number of buttons supported */
1196
1197 const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
1198 const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
1199 const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
1200 const DWORD LEFT_RIGHT = LEFT | RIGHT;
1201
1202 int nButton;
1203
1204 if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons))
1205 cButtons = 2;
1206
1207 if (!g_fMouseAvail || !g_fMouseActive)
1208 {
1209 g_nMouseClick = -1;
1210 return FALSE;
1211 }
1212
1213 /* get a spurious MOUSE_EVENT immediately after receiving focus; ignore */
1214 if (g_fJustGotFocus)
1215 {
1216 g_fJustGotFocus = FALSE;
1217 return FALSE;
1218 }
1219
1220 /* unprocessed mouse click? */
1221 if (g_nMouseClick != -1)
1222 return TRUE;
1223
1224 nButton = -1;
1225 g_xMouse = pmer->dwMousePosition.X;
1226 g_yMouse = pmer->dwMousePosition.Y;
1227
1228 if (pmer->dwEventFlags == MOUSE_MOVED)
1229 {
1230 /* ignore MOUSE_MOVED events if (x, y) hasn't changed. (We get these
1231 * events even when the mouse moves only within a char cell.) */
1232 if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse)
1233 return FALSE;
1234 }
1235
1236 /* If no buttons are pressed... */
1237 if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0)
1238 {
1239 /* If the last thing returned was MOUSE_RELEASE, ignore this */
1240 if (s_fReleased)
1241 return FALSE;
1242
1243 nButton = MOUSE_RELEASE;
1244 s_fReleased = TRUE;
1245 }
1246 else /* one or more buttons pressed */
1247 {
1248 /* on a 2-button mouse, hold down left and right buttons
1249 * simultaneously to get MIDDLE. */
1250
1251 if (cButtons == 2 && s_nOldButton != MOUSE_DRAG)
1252 {
1253 DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
1254
1255 /* if either left or right button only is pressed, see if the
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02001256 * next mouse event has both of them pressed */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257 if (dwLR == LEFT || dwLR == RIGHT)
1258 {
1259 for (;;)
1260 {
1261 /* wait a short time for next input event */
1262 if (WaitForSingleObject(g_hConIn, p_mouset / 3)
1263 != WAIT_OBJECT_0)
1264 break;
1265 else
1266 {
1267 DWORD cRecords = 0;
1268 INPUT_RECORD ir;
1269 MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
1270
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001271 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001272
1273 if (cRecords == 0 || ir.EventType != MOUSE_EVENT
1274 || !(pmer2->dwButtonState & LEFT_RIGHT))
1275 break;
1276 else
1277 {
1278 if (pmer2->dwEventFlags != MOUSE_MOVED)
1279 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001280 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281
1282 return decode_mouse_event(pmer2);
1283 }
1284 else if (s_xOldMouse == pmer2->dwMousePosition.X &&
1285 s_yOldMouse == pmer2->dwMousePosition.Y)
1286 {
1287 /* throw away spurious mouse move */
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001288 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001289
1290 /* are there any more mouse events in queue? */
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001291 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001292
1293 if (cRecords==0 || ir.EventType != MOUSE_EVENT)
1294 break;
1295 }
1296 else
1297 break;
1298 }
1299 }
1300 }
1301 }
1302 }
1303
1304 if (s_fNextIsMiddle)
1305 {
1306 nButton = (pmer->dwEventFlags == MOUSE_MOVED)
1307 ? MOUSE_DRAG : MOUSE_MIDDLE;
1308 s_fNextIsMiddle = FALSE;
1309 }
1310 else if (cButtons == 2 &&
1311 ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
1312 {
1313 nButton = MOUSE_MIDDLE;
1314
1315 if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED)
1316 {
1317 s_fNextIsMiddle = TRUE;
1318 nButton = MOUSE_RELEASE;
1319 }
1320 }
1321 else if ((pmer->dwButtonState & LEFT) == LEFT)
1322 nButton = MOUSE_LEFT;
1323 else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
1324 nButton = MOUSE_MIDDLE;
1325 else if ((pmer->dwButtonState & RIGHT) == RIGHT)
1326 nButton = MOUSE_RIGHT;
1327
1328 if (! s_fReleased && ! s_fNextIsMiddle
1329 && nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG)
1330 return FALSE;
1331
1332 s_fReleased = s_fNextIsMiddle;
1333 }
1334
1335 if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK)
1336 {
1337 /* button pressed or released, without mouse moving */
1338 if (nButton != -1 && nButton != MOUSE_RELEASE)
1339 {
1340 DWORD dwCurrentTime = GetTickCount();
1341
1342 if (s_xOldMouse != g_xMouse
1343 || s_yOldMouse != g_yMouse
1344 || s_nOldButton != nButton
1345 || s_old_topline != curwin->w_topline
1346#ifdef FEAT_DIFF
1347 || s_old_topfill != curwin->w_topfill
1348#endif
1349 || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset)
1350 {
1351 s_cClicks = 1;
1352 }
1353 else if (++s_cClicks > 4)
1354 {
1355 s_cClicks = 1;
1356 }
1357
1358 s_dwLastClickTime = dwCurrentTime;
1359 }
1360 }
1361 else if (pmer->dwEventFlags == MOUSE_MOVED)
1362 {
1363 if (nButton != -1 && nButton != MOUSE_RELEASE)
1364 nButton = MOUSE_DRAG;
1365
1366 s_cClicks = 1;
1367 }
1368
1369 if (nButton == -1)
1370 return FALSE;
1371
1372 if (nButton != MOUSE_RELEASE)
1373 s_nOldButton = nButton;
1374
1375 g_nMouseClick = nButton;
1376
1377 if (pmer->dwControlKeyState & SHIFT_PRESSED)
1378 g_nMouseClick |= MOUSE_SHIFT;
1379 if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
1380 g_nMouseClick |= MOUSE_CTRL;
1381 if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
1382 g_nMouseClick |= MOUSE_ALT;
1383
1384 if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE)
1385 SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
1386
1387 /* only pass on interesting (i.e., different) mouse events */
1388 if (s_xOldMouse == g_xMouse
1389 && s_yOldMouse == g_yMouse
1390 && s_nOldMouseClick == g_nMouseClick)
1391 {
1392 g_nMouseClick = -1;
1393 return FALSE;
1394 }
1395
1396 s_xOldMouse = g_xMouse;
1397 s_yOldMouse = g_yMouse;
1398 s_old_topline = curwin->w_topline;
1399#ifdef FEAT_DIFF
1400 s_old_topfill = curwin->w_topfill;
1401#endif
1402 s_nOldMouseClick = g_nMouseClick;
1403
1404 return TRUE;
1405}
1406
1407# endif /* FEAT_GUI_W32 */
1408#endif /* FEAT_MOUSE */
1409
1410
1411#ifdef MCH_CURSOR_SHAPE
1412/*
1413 * Set the shape of the cursor.
1414 * 'thickness' can be from 1 (thin) to 99 (block)
1415 */
1416 static void
1417mch_set_cursor_shape(int thickness)
1418{
1419 CONSOLE_CURSOR_INFO ConsoleCursorInfo;
1420 ConsoleCursorInfo.dwSize = thickness;
1421 ConsoleCursorInfo.bVisible = s_cursor_visible;
1422
1423 SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo);
1424 if (s_cursor_visible)
1425 SetConsoleCursorPosition(g_hConOut, g_coord);
1426}
1427
1428 void
1429mch_update_cursor(void)
1430{
1431 int idx;
1432 int thickness;
1433
1434 /*
1435 * How the cursor is drawn depends on the current mode.
1436 */
1437 idx = get_shape_idx(FALSE);
1438
1439 if (shape_table[idx].shape == SHAPE_BLOCK)
1440 thickness = 99; /* 100 doesn't work on W95 */
1441 else
1442 thickness = shape_table[idx].percentage;
1443 mch_set_cursor_shape(thickness);
1444}
1445#endif
1446
1447#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1448/*
1449 * Handle FOCUS_EVENT.
1450 */
1451 static void
1452handle_focus_event(INPUT_RECORD ir)
1453{
1454 g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
1455 ui_focus_change((int)g_fJustGotFocus);
1456}
1457
1458/*
1459 * Wait until console input from keyboard or mouse is available,
1460 * or the time is up.
1461 * Return TRUE if something is available FALSE if not.
1462 */
1463 static int
1464WaitForChar(long msec)
1465{
1466 DWORD dwNow = 0, dwEndTime = 0;
1467 INPUT_RECORD ir;
1468 DWORD cRecords;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001469 WCHAR ch, ch2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001470
1471 if (msec > 0)
1472 /* Wait until the specified time has elapsed. */
1473 dwEndTime = GetTickCount() + msec;
1474 else if (msec < 0)
1475 /* Wait forever. */
1476 dwEndTime = INFINITE;
1477
1478 /* We need to loop until the end of the time period, because
1479 * we might get multiple unusable mouse events in that time.
1480 */
1481 for (;;)
1482 {
Bram Moolenaarca568ae2016-02-01 21:32:58 +01001483#ifdef MESSAGE_QUEUE
1484 parse_queued_messages();
1485#endif
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001486#ifdef FEAT_MZSCHEME
1487 mzvim_check_threads();
1488#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489#ifdef FEAT_CLIENTSERVER
1490 serverProcessPendingMessages();
1491#endif
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001492
1493#ifdef FEAT_CHANNEL
Bram Moolenaar4d919d72016-02-05 22:36:41 +01001494 if (handle_channel_event())
1495 return TRUE;
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001496#endif
1497
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498 if (0
1499#ifdef FEAT_MOUSE
1500 || g_nMouseClick != -1
1501#endif
1502#ifdef FEAT_CLIENTSERVER
1503 || input_available()
1504#endif
1505 )
1506 return TRUE;
1507
1508 if (msec > 0)
1509 {
Bram Moolenaarb7512b72013-08-10 12:45:09 +02001510 /* If the specified wait time has passed, return. Beware that
1511 * GetTickCount() may wrap around (overflow). */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001512 dwNow = GetTickCount();
Bram Moolenaarb7512b72013-08-10 12:45:09 +02001513 if ((int)(dwNow - dwEndTime) >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001514 break;
1515 }
1516 if (msec != 0)
1517 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001518 DWORD dwWaitTime = dwEndTime - dwNow;
1519
1520#ifdef FEAT_MZSCHEME
1521 if (mzthreads_allowed() && p_mzq > 0
1522 && (msec < 0 || (long)dwWaitTime > p_mzq))
1523 dwWaitTime = p_mzq; /* don't wait longer than 'mzquantum' */
1524#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001525#ifdef FEAT_CLIENTSERVER
1526 /* Wait for either an event on the console input or a message in
1527 * the client-server window. */
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +02001528 if (msg_wait_for_multiple_objects(1, &g_hConIn, FALSE,
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001529 dwWaitTime, QS_SENDMESSAGE) != WAIT_OBJECT_0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001530#else
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +02001531 if (wait_for_single_object(g_hConIn, dwWaitTime) != WAIT_OBJECT_0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001532#endif
1533 continue;
1534 }
1535
1536 cRecords = 0;
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001537 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001538
1539#ifdef FEAT_MBYTE_IME
1540 if (State & CMDLINE && msg_row == Rows - 1)
1541 {
1542 CONSOLE_SCREEN_BUFFER_INFO csbi;
1543
1544 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1545 {
1546 if (csbi.dwCursorPosition.Y != msg_row)
1547 {
1548 /* The screen is now messed up, must redraw the
1549 * command line and later all the windows. */
1550 redraw_all_later(CLEAR);
1551 cmdline_row -= (msg_row - csbi.dwCursorPosition.Y);
1552 redrawcmd();
1553 }
1554 }
1555 }
1556#endif
1557
1558 if (cRecords > 0)
1559 {
1560 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
1561 {
1562#ifdef FEAT_MBYTE_IME
1563 /* Windows IME sends two '\n's with only one 'ENTER'. First:
1564 * wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001565 if (ir.Event.KeyEvent.UChar == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001566 && ir.Event.KeyEvent.wVirtualKeyCode == 13)
1567 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001568 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001569 continue;
1570 }
1571#endif
1572 if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
1573 NULL, FALSE))
1574 return TRUE;
1575 }
1576
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001577 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001578
1579 if (ir.EventType == FOCUS_EVENT)
1580 handle_focus_event(ir);
1581 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1582 shell_resized();
1583#ifdef FEAT_MOUSE
1584 else if (ir.EventType == MOUSE_EVENT
1585 && decode_mouse_event(&ir.Event.MouseEvent))
1586 return TRUE;
1587#endif
1588 }
1589 else if (msec == 0)
1590 break;
1591 }
1592
1593#ifdef FEAT_CLIENTSERVER
1594 /* Something might have been received while we were waiting. */
1595 if (input_available())
1596 return TRUE;
1597#endif
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001598
Bram Moolenaar071d4272004-06-13 20:20:40 +00001599 return FALSE;
1600}
1601
1602#ifndef FEAT_GUI_MSWIN
1603/*
1604 * return non-zero if a character is available
1605 */
1606 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001607mch_char_avail(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001608{
1609 return WaitForChar(0L);
1610}
1611#endif
1612
1613/*
1614 * Create the console input. Used when reading stdin doesn't work.
1615 */
1616 static void
1617create_conin(void)
1618{
1619 g_hConIn = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
1620 FILE_SHARE_READ|FILE_SHARE_WRITE,
1621 (LPSECURITY_ATTRIBUTES) NULL,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001622 OPEN_EXISTING, 0, (HANDLE)NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001623 did_create_conin = TRUE;
1624}
1625
1626/*
1627 * Get a keystroke or a mouse event
1628 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001629 static WCHAR
1630tgetch(int *pmodifiers, WCHAR *pch2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001631{
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001632 WCHAR ch;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001633
1634 for (;;)
1635 {
1636 INPUT_RECORD ir;
1637 DWORD cRecords = 0;
1638
1639#ifdef FEAT_CLIENTSERVER
1640 (void)WaitForChar(-1L);
1641 if (input_available())
1642 return 0;
1643# ifdef FEAT_MOUSE
1644 if (g_nMouseClick != -1)
1645 return 0;
1646# endif
1647#endif
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001648 if (read_console_input(g_hConIn, &ir, 1, &cRecords) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001649 {
1650 if (did_create_conin)
1651 read_error_exit();
1652 create_conin();
1653 continue;
1654 }
1655
1656 if (ir.EventType == KEY_EVENT)
1657 {
1658 if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2,
1659 pmodifiers, TRUE))
1660 return ch;
1661 }
1662 else if (ir.EventType == FOCUS_EVENT)
1663 handle_focus_event(ir);
1664 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1665 shell_resized();
1666#ifdef FEAT_MOUSE
1667 else if (ir.EventType == MOUSE_EVENT)
1668 {
1669 if (decode_mouse_event(&ir.Event.MouseEvent))
1670 return 0;
1671 }
1672#endif
1673 }
1674}
1675#endif /* !FEAT_GUI_W32 */
1676
1677
1678/*
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02001679 * mch_inchar(): low-level input function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001680 * Get one or more characters from the keyboard or the mouse.
1681 * If time == 0, do not wait for characters.
1682 * If time == n, wait a short time for characters.
1683 * If time == -1, wait forever for characters.
1684 * Returns the number of characters read into buf.
1685 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00001686/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00001687 int
1688mch_inchar(
1689 char_u *buf,
1690 int maxlen,
1691 long time,
1692 int tb_change_cnt)
1693{
1694#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1695
1696 int len;
1697 int c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001698#define TYPEAHEADLEN 20
1699 static char_u typeahead[TYPEAHEADLEN]; /* previously typed bytes. */
1700 static int typeaheadlen = 0;
1701
1702 /* First use any typeahead that was kept because "buf" was too small. */
1703 if (typeaheadlen > 0)
1704 goto theend;
1705
1706#ifdef FEAT_SNIFF
1707 if (want_sniff_request)
1708 {
1709 if (sniff_request_waiting)
1710 {
1711 /* return K_SNIFF */
1712 typeahead[typeaheadlen++] = CSI;
1713 typeahead[typeaheadlen++] = (char_u)KS_EXTRA;
1714 typeahead[typeaheadlen++] = (char_u)KE_SNIFF;
1715 sniff_request_waiting = 0;
1716 want_sniff_request = 0;
1717 goto theend;
1718 }
1719 else if (time < 0 || time > 250)
1720 {
1721 /* don't wait too long, a request might be pending */
1722 time = 250;
1723 }
1724 }
1725#endif
1726
1727 if (time >= 0)
1728 {
1729 if (!WaitForChar(time)) /* no character available */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001731 }
1732 else /* time == -1, wait forever */
1733 {
1734 mch_set_winsize_now(); /* Allow winsize changes from now on */
1735
Bram Moolenaar3918c952005-03-15 22:34:55 +00001736 /*
1737 * If there is no character available within 2 seconds (default)
1738 * write the autoscript file to disk. Or cause the CursorHold event
1739 * to be triggered.
1740 */
1741 if (!WaitForChar(p_ut))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001742 {
1743#ifdef FEAT_AUTOCMD
Bram Moolenaard35f9712005-12-18 22:02:33 +00001744 if (trigger_cursorhold() && maxlen >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745 {
Bram Moolenaar3918c952005-03-15 22:34:55 +00001746 buf[0] = K_SPECIAL;
1747 buf[1] = KS_EXTRA;
1748 buf[2] = (int)KE_CURSORHOLD;
1749 return 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001750 }
1751#endif
Bram Moolenaar702517d2005-06-27 22:34:07 +00001752 before_blocking();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001753 }
1754 }
1755
1756 /*
1757 * Try to read as many characters as there are, until the buffer is full.
1758 */
1759
1760 /* we will get at least one key. Get more if they are available. */
1761 g_fCBrkPressed = FALSE;
1762
1763#ifdef MCH_WRITE_DUMP
1764 if (fdDump)
1765 fputc('[', fdDump);
1766#endif
1767
1768 /* Keep looping until there is something in the typeahead buffer and more
1769 * to get and still room in the buffer (up to two bytes for a char and
1770 * three bytes for a modifier). */
1771 while ((typeaheadlen == 0 || WaitForChar(0L))
1772 && typeaheadlen + 5 <= TYPEAHEADLEN)
1773 {
1774 if (typebuf_changed(tb_change_cnt))
1775 {
1776 /* "buf" may be invalid now if a client put something in the
1777 * typeahead buffer and "buf" is in the typeahead buffer. */
1778 typeaheadlen = 0;
1779 break;
1780 }
1781#ifdef FEAT_MOUSE
1782 if (g_nMouseClick != -1)
1783 {
1784# ifdef MCH_WRITE_DUMP
1785 if (fdDump)
1786 fprintf(fdDump, "{%02x @ %d, %d}",
1787 g_nMouseClick, g_xMouse, g_yMouse);
1788# endif
1789 typeahead[typeaheadlen++] = ESC + 128;
1790 typeahead[typeaheadlen++] = 'M';
1791 typeahead[typeaheadlen++] = g_nMouseClick;
1792 typeahead[typeaheadlen++] = g_xMouse + '!';
1793 typeahead[typeaheadlen++] = g_yMouse + '!';
1794 g_nMouseClick = -1;
1795 }
1796 else
1797#endif
1798 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001799 WCHAR ch2 = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800 int modifiers = 0;
1801
1802 c = tgetch(&modifiers, &ch2);
1803
1804 if (typebuf_changed(tb_change_cnt))
1805 {
1806 /* "buf" may be invalid now if a client put something in the
1807 * typeahead buffer and "buf" is in the typeahead buffer. */
1808 typeaheadlen = 0;
1809 break;
1810 }
1811
1812 if (c == Ctrl_C && ctrl_c_interrupts)
1813 {
1814#if defined(FEAT_CLIENTSERVER)
1815 trash_input_buf();
1816#endif
1817 got_int = TRUE;
1818 }
1819
1820#ifdef FEAT_MOUSE
1821 if (g_nMouseClick == -1)
1822#endif
1823 {
1824 int n = 1;
Bram Moolenaar45500912014-07-09 20:51:07 +02001825 int conv = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001826
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001827#ifdef FEAT_MBYTE
1828 if (ch2 == NUL)
1829 {
1830 int i;
1831 char_u *p;
1832 WCHAR ch[2];
1833
1834 ch[0] = c;
1835 if (c >= 0xD800 && c <= 0xDBFF) /* High surrogate */
1836 {
1837 ch[1] = tgetch(&modifiers, &ch2);
1838 n++;
1839 }
1840 p = utf16_to_enc(ch, &n);
1841 if (p != NULL)
1842 {
1843 for (i = 0; i < n; i++)
1844 typeahead[typeaheadlen + i] = p[i];
1845 vim_free(p);
1846 }
1847 }
1848 else
1849#endif
1850 typeahead[typeaheadlen] = c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001851 if (ch2 != NUL)
1852 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001853 typeahead[typeaheadlen + n] = 3;
1854 typeahead[typeaheadlen + n + 1] = (char_u)ch2;
Bram Moolenaar45500912014-07-09 20:51:07 +02001855 n += 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001856 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001857
Bram Moolenaar45500912014-07-09 20:51:07 +02001858 if (conv)
1859 {
1860 char_u *p = typeahead + typeaheadlen;
Bram Moolenaar45500912014-07-09 20:51:07 +02001861
Bram Moolenaar1ec4dd42015-01-20 19:39:35 +01001862 if (*p != K_NUL)
Bram Moolenaar45500912014-07-09 20:51:07 +02001863 {
Bram Moolenaar1ec4dd42015-01-20 19:39:35 +01001864 char_u *e = typeahead + TYPEAHEADLEN;
1865
1866 while (*p && p < e)
Bram Moolenaar45500912014-07-09 20:51:07 +02001867 {
Bram Moolenaar1ec4dd42015-01-20 19:39:35 +01001868 if (*p == K_NUL)
1869 {
1870 ++p;
1871 mch_memmove(p + 1, p, ((size_t)(e - p)) - 1);
1872 *p = 3;
1873 ++n;
1874 }
Bram Moolenaar45500912014-07-09 20:51:07 +02001875 ++p;
Bram Moolenaar45500912014-07-09 20:51:07 +02001876 }
Bram Moolenaar45500912014-07-09 20:51:07 +02001877 }
1878 }
1879
Bram Moolenaar071d4272004-06-13 20:20:40 +00001880 /* Use the ALT key to set the 8th bit of the character
1881 * when it's one byte, the 8th bit isn't set yet and not
1882 * using a double-byte encoding (would become a lead
1883 * byte). */
1884 if ((modifiers & MOD_MASK_ALT)
1885 && n == 1
1886 && (typeahead[typeaheadlen] & 0x80) == 0
1887#ifdef FEAT_MBYTE
1888 && !enc_dbcs
1889#endif
1890 )
1891 {
Bram Moolenaar85a3e5c2007-11-20 16:22:16 +00001892#ifdef FEAT_MBYTE
1893 n = (*mb_char2bytes)(typeahead[typeaheadlen] | 0x80,
1894 typeahead + typeaheadlen);
1895#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001896 typeahead[typeaheadlen] |= 0x80;
Bram Moolenaar85a3e5c2007-11-20 16:22:16 +00001897#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001898 modifiers &= ~MOD_MASK_ALT;
1899 }
1900
1901 if (modifiers != 0)
1902 {
1903 /* Prepend modifiers to the character. */
1904 mch_memmove(typeahead + typeaheadlen + 3,
1905 typeahead + typeaheadlen, n);
1906 typeahead[typeaheadlen++] = K_SPECIAL;
1907 typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
1908 typeahead[typeaheadlen++] = modifiers;
1909 }
1910
1911 typeaheadlen += n;
1912
1913#ifdef MCH_WRITE_DUMP
1914 if (fdDump)
1915 fputc(c, fdDump);
1916#endif
1917 }
1918 }
1919 }
1920
1921#ifdef MCH_WRITE_DUMP
1922 if (fdDump)
1923 {
1924 fputs("]\n", fdDump);
1925 fflush(fdDump);
1926 }
1927#endif
1928
Bram Moolenaar071d4272004-06-13 20:20:40 +00001929theend:
1930 /* Move typeahead to "buf", as much as fits. */
1931 len = 0;
1932 while (len < maxlen && typeaheadlen > 0)
1933 {
1934 buf[len++] = typeahead[0];
1935 mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
1936 }
1937 return len;
1938
1939#else /* FEAT_GUI_W32 */
1940 return 0;
1941#endif /* FEAT_GUI_W32 */
1942}
1943
Bram Moolenaar82881492012-11-20 16:53:39 +01001944#ifndef PROTO
1945# ifndef __MINGW32__
1946# include <shellapi.h> /* required for FindExecutable() */
1947# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001948#endif
1949
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001950/*
1951 * Return TRUE if "name" is in $PATH.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00001952 * TODO: Should somehow check if it's really executable.
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001953 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001954 static int
Bram Moolenaarc7f02552014-04-01 21:00:59 +02001955executable_exists(char *name, char_u **path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001956{
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001957 char *dum;
1958 char fname[_MAX_PATH];
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02001959 char *curpath, *newpath;
1960 long n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001961
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001962#ifdef FEAT_MBYTE
1963 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001964 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001965 WCHAR *p = enc_to_utf16((char_u *)name, NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001966 WCHAR fnamew[_MAX_PATH];
1967 WCHAR *dumw;
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02001968 WCHAR *wcurpath, *wnewpath;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001969
1970 if (p != NULL)
1971 {
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02001972 wcurpath = _wgetenv(L"PATH");
1973 wnewpath = (WCHAR*)alloc((unsigned)(wcslen(wcurpath) + 3)
1974 * sizeof(WCHAR));
1975 if (wnewpath == NULL)
1976 return FALSE;
1977 wcscpy(wnewpath, L".;");
1978 wcscat(wnewpath, wcurpath);
1979 n = (long)SearchPathW(wnewpath, p, NULL, _MAX_PATH, fnamew, &dumw);
1980 vim_free(wnewpath);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001981 vim_free(p);
1982 if (n > 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
1983 {
1984 if (n == 0)
1985 return FALSE;
1986 if (GetFileAttributesW(fnamew) & FILE_ATTRIBUTE_DIRECTORY)
1987 return FALSE;
Bram Moolenaarc7f02552014-04-01 21:00:59 +02001988 if (path != NULL)
1989 *path = utf16_to_enc(fnamew, NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001990 return TRUE;
1991 }
1992 /* Retry with non-wide function (for Windows 98). */
1993 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001994 }
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001995#endif
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02001996
1997 curpath = getenv("PATH");
1998 newpath = (char*)alloc((unsigned)(STRLEN(curpath) + 3));
1999 if (newpath == NULL)
2000 return FALSE;
2001 STRCPY(newpath, ".;");
2002 STRCAT(newpath, curpath);
2003 n = (long)SearchPath(newpath, name, NULL, _MAX_PATH, fname, &dum);
2004 vim_free(newpath);
2005 if (n == 0)
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002006 return FALSE;
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002007 if (mch_isdir((char_u *)fname))
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002008 return FALSE;
Bram Moolenaarc7f02552014-04-01 21:00:59 +02002009 if (path != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002010 *path = vim_strsave((char_u *)fname);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002011 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012}
2013
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002014#if ((defined(__MINGW32__) || defined (__CYGWIN32__)) && \
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02002015 __MSVCRT_VERSION__ >= 0x800) || (defined(_MSC_VER) && _MSC_VER >= 1400)
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002016/*
2017 * Bad parameter handler.
2018 *
2019 * Certain MS CRT functions will intentionally crash when passed invalid
2020 * parameters to highlight possible security holes. Setting this function as
2021 * the bad parameter handler will prevent the crash.
2022 *
2023 * In debug builds the parameters contain CRT information that might help track
2024 * down the source of a problem, but in non-debug builds the arguments are all
2025 * NULL/0. Debug builds will also produce assert dialogs from the CRT, it is
2026 * worth allowing these to make debugging of issues easier.
2027 */
2028 static void
2029bad_param_handler(const wchar_t *expression,
2030 const wchar_t *function,
2031 const wchar_t *file,
2032 unsigned int line,
2033 uintptr_t pReserved)
2034{
2035}
2036
2037# define SET_INVALID_PARAM_HANDLER \
2038 ((void)_set_invalid_parameter_handler(bad_param_handler))
2039#else
2040# define SET_INVALID_PARAM_HANDLER
2041#endif
2042
Bram Moolenaar071d4272004-06-13 20:20:40 +00002043#ifdef FEAT_GUI_W32
2044
2045/*
2046 * GUI version of mch_init().
2047 */
2048 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002049mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002050{
2051#ifndef __MINGW32__
2052 extern int _fmode;
2053#endif
2054
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002055 /* Silently handle invalid parameters to CRT functions */
2056 SET_INVALID_PARAM_HANDLER;
2057
Bram Moolenaar071d4272004-06-13 20:20:40 +00002058 /* Let critical errors result in a failure, not in a dialog box. Required
2059 * for the timestamp test to work on removed floppies. */
2060 SetErrorMode(SEM_FAILCRITICALERRORS);
2061
2062 _fmode = O_BINARY; /* we do our own CR-LF translation */
2063
2064 /* Specify window size. Is there a place to get the default from? */
2065 Rows = 25;
2066 Columns = 80;
2067
2068 /* Look for 'vimrun' */
2069 if (!gui_is_win32s())
2070 {
2071 char_u vimrun_location[_MAX_PATH + 4];
2072
2073 /* First try in same directory as gvim.exe */
2074 STRCPY(vimrun_location, exe_name);
2075 STRCPY(gettail(vimrun_location), "vimrun.exe");
2076 if (mch_getperm(vimrun_location) >= 0)
2077 {
2078 if (*skiptowhite(vimrun_location) != NUL)
2079 {
2080 /* Enclose path with white space in double quotes. */
2081 mch_memmove(vimrun_location + 1, vimrun_location,
2082 STRLEN(vimrun_location) + 1);
2083 *vimrun_location = '"';
2084 STRCPY(gettail(vimrun_location), "vimrun\" ");
2085 }
2086 else
2087 STRCPY(gettail(vimrun_location), "vimrun ");
2088
2089 vimrun_path = (char *)vim_strsave(vimrun_location);
2090 s_dont_use_vimrun = FALSE;
2091 }
Bram Moolenaarc7f02552014-04-01 21:00:59 +02002092 else if (executable_exists("vimrun.exe", NULL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002093 s_dont_use_vimrun = FALSE;
2094
2095 /* Don't give the warning for a missing vimrun.exe right now, but only
2096 * when vimrun was supposed to be used. Don't bother people that do
2097 * not need vimrun.exe. */
2098 if (s_dont_use_vimrun)
2099 need_vimrun_warning = TRUE;
2100 }
2101
2102 /*
2103 * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
2104 * Otherwise the default "findstr /n" is used.
2105 */
Bram Moolenaarc7f02552014-04-01 21:00:59 +02002106 if (!executable_exists("findstr.exe", NULL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002107 set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
2108
2109#ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002110 win_clip_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002111#endif
2112}
2113
2114
2115#else /* FEAT_GUI_W32 */
2116
2117#define SRWIDTH(sr) ((sr).Right - (sr).Left + 1)
2118#define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1)
2119
2120/*
2121 * ClearConsoleBuffer()
2122 * Description:
2123 * Clears the entire contents of the console screen buffer, using the
2124 * specified attribute.
2125 * Returns:
2126 * TRUE on success
2127 */
2128 static BOOL
2129ClearConsoleBuffer(WORD wAttribute)
2130{
2131 CONSOLE_SCREEN_BUFFER_INFO csbi;
2132 COORD coord;
2133 DWORD NumCells, dummy;
2134
2135 if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2136 return FALSE;
2137
2138 NumCells = csbi.dwSize.X * csbi.dwSize.Y;
2139 coord.X = 0;
2140 coord.Y = 0;
2141 if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells,
2142 coord, &dummy))
2143 {
2144 return FALSE;
2145 }
2146 if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells,
2147 coord, &dummy))
2148 {
2149 return FALSE;
2150 }
2151
2152 return TRUE;
2153}
2154
2155/*
2156 * FitConsoleWindow()
2157 * Description:
2158 * Checks if the console window will fit within given buffer dimensions.
2159 * Also, if requested, will shrink the window to fit.
2160 * Returns:
2161 * TRUE on success
2162 */
2163 static BOOL
2164FitConsoleWindow(
2165 COORD dwBufferSize,
2166 BOOL WantAdjust)
2167{
2168 CONSOLE_SCREEN_BUFFER_INFO csbi;
2169 COORD dwWindowSize;
2170 BOOL NeedAdjust = FALSE;
2171
2172 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2173 {
2174 /*
2175 * A buffer resize will fail if the current console window does
2176 * not lie completely within that buffer. To avoid this, we might
2177 * have to move and possibly shrink the window.
2178 */
2179 if (csbi.srWindow.Right >= dwBufferSize.X)
2180 {
2181 dwWindowSize.X = SRWIDTH(csbi.srWindow);
2182 if (dwWindowSize.X > dwBufferSize.X)
2183 dwWindowSize.X = dwBufferSize.X;
2184 csbi.srWindow.Right = dwBufferSize.X - 1;
2185 csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X;
2186 NeedAdjust = TRUE;
2187 }
2188 if (csbi.srWindow.Bottom >= dwBufferSize.Y)
2189 {
2190 dwWindowSize.Y = SRHEIGHT(csbi.srWindow);
2191 if (dwWindowSize.Y > dwBufferSize.Y)
2192 dwWindowSize.Y = dwBufferSize.Y;
2193 csbi.srWindow.Bottom = dwBufferSize.Y - 1;
2194 csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y;
2195 NeedAdjust = TRUE;
2196 }
2197 if (NeedAdjust && WantAdjust)
2198 {
2199 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow))
2200 return FALSE;
2201 }
2202 return TRUE;
2203 }
2204
2205 return FALSE;
2206}
2207
2208typedef struct ConsoleBufferStruct
2209{
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002210 BOOL IsValid;
2211 CONSOLE_SCREEN_BUFFER_INFO Info;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002212 PCHAR_INFO Buffer;
2213 COORD BufferSize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002214} ConsoleBuffer;
2215
2216/*
2217 * SaveConsoleBuffer()
2218 * Description:
2219 * Saves important information about the console buffer, including the
2220 * actual buffer contents. The saved information is suitable for later
2221 * restoration by RestoreConsoleBuffer().
2222 * Returns:
2223 * TRUE if all information was saved; FALSE otherwise
2224 * If FALSE, still sets cb->IsValid if buffer characteristics were saved.
2225 */
2226 static BOOL
2227SaveConsoleBuffer(
2228 ConsoleBuffer *cb)
2229{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002230 DWORD NumCells;
2231 COORD BufferCoord;
2232 SMALL_RECT ReadRegion;
2233 WORD Y, Y_incr;
2234
Bram Moolenaar071d4272004-06-13 20:20:40 +00002235 if (cb == NULL)
2236 return FALSE;
2237
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002238 if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002239 {
2240 cb->IsValid = FALSE;
2241 return FALSE;
2242 }
2243 cb->IsValid = TRUE;
2244
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002245 /*
2246 * Allocate a buffer large enough to hold the entire console screen
2247 * buffer. If this ConsoleBuffer structure has already been initialized
2248 * with a buffer of the correct size, then just use that one.
2249 */
2250 if (!cb->IsValid || cb->Buffer == NULL ||
2251 cb->BufferSize.X != cb->Info.dwSize.X ||
2252 cb->BufferSize.Y != cb->Info.dwSize.Y)
2253 {
2254 cb->BufferSize.X = cb->Info.dwSize.X;
2255 cb->BufferSize.Y = cb->Info.dwSize.Y;
2256 NumCells = cb->BufferSize.X * cb->BufferSize.Y;
2257 vim_free(cb->Buffer);
2258 cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
2259 if (cb->Buffer == NULL)
2260 return FALSE;
2261 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002262
2263 /*
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002264 * We will now copy the console screen buffer into our buffer.
2265 * ReadConsoleOutput() seems to be limited as far as how much you
2266 * can read at a time. Empirically, this number seems to be about
2267 * 12000 cells (rows * columns). Start at position (0, 0) and copy
2268 * in chunks until it is all copied. The chunks will all have the
2269 * same horizontal characteristics, so initialize them now. The
2270 * height of each chunk will be (12000 / width).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271 */
Bram Moolenaar61594242015-09-01 20:23:37 +02002272 BufferCoord.X = 0;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002273 ReadRegion.Left = 0;
2274 ReadRegion.Right = cb->Info.dwSize.X - 1;
2275 Y_incr = 12000 / cb->Info.dwSize.X;
2276 for (Y = 0; Y < cb->BufferSize.Y; Y += Y_incr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002277 {
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002278 /*
2279 * Read into position (0, Y) in our buffer.
2280 */
2281 BufferCoord.Y = Y;
2282 /*
2283 * Read the region whose top left corner is (0, Y) and whose bottom
2284 * right corner is (width - 1, Y + Y_incr - 1). This should define
2285 * a region of size width by Y_incr. Don't worry if this region is
2286 * too large for the remaining buffer; it will be cropped.
2287 */
2288 ReadRegion.Top = Y;
2289 ReadRegion.Bottom = Y + Y_incr - 1;
2290 if (!ReadConsoleOutput(g_hConOut, /* output handle */
2291 cb->Buffer, /* our buffer */
2292 cb->BufferSize, /* dimensions of our buffer */
2293 BufferCoord, /* offset in our buffer */
2294 &ReadRegion)) /* region to save */
2295 {
2296 vim_free(cb->Buffer);
2297 cb->Buffer = NULL;
2298 return FALSE;
2299 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002300 }
2301
2302 return TRUE;
2303}
2304
2305/*
2306 * RestoreConsoleBuffer()
2307 * Description:
2308 * Restores important information about the console buffer, including the
2309 * actual buffer contents, if desired. The information to restore is in
2310 * the same format used by SaveConsoleBuffer().
2311 * Returns:
2312 * TRUE on success
2313 */
2314 static BOOL
2315RestoreConsoleBuffer(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002316 ConsoleBuffer *cb,
2317 BOOL RestoreScreen)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002318{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002319 COORD BufferCoord;
2320 SMALL_RECT WriteRegion;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002321
2322 if (cb == NULL || !cb->IsValid)
2323 return FALSE;
2324
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002325 /*
2326 * Before restoring the buffer contents, clear the current buffer, and
2327 * restore the cursor position and window information. Doing this now
2328 * prevents old buffer contents from "flashing" onto the screen.
2329 */
2330 if (RestoreScreen)
2331 ClearConsoleBuffer(cb->Info.wAttributes);
2332
2333 FitConsoleWindow(cb->Info.dwSize, TRUE);
2334 if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
2335 return FALSE;
2336 if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
2337 return FALSE;
2338
2339 if (!RestoreScreen)
2340 {
2341 /*
2342 * No need to restore the screen buffer contents, so we're done.
2343 */
2344 return TRUE;
2345 }
2346
2347 if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
2348 return FALSE;
2349 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
2350 return FALSE;
2351
2352 /*
2353 * Restore the screen buffer contents.
2354 */
2355 if (cb->Buffer != NULL)
2356 {
2357 BufferCoord.X = 0;
2358 BufferCoord.Y = 0;
2359 WriteRegion.Left = 0;
2360 WriteRegion.Top = 0;
2361 WriteRegion.Right = cb->Info.dwSize.X - 1;
2362 WriteRegion.Bottom = cb->Info.dwSize.Y - 1;
2363 if (!WriteConsoleOutput(g_hConOut, /* output handle */
2364 cb->Buffer, /* our buffer */
2365 cb->BufferSize, /* dimensions of our buffer */
2366 BufferCoord, /* offset in our buffer */
2367 &WriteRegion)) /* region to restore */
2368 {
2369 return FALSE;
2370 }
2371 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002372
2373 return TRUE;
2374}
2375
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002376#define FEAT_RESTORE_ORIG_SCREEN
2377#ifdef FEAT_RESTORE_ORIG_SCREEN
2378static ConsoleBuffer g_cbOrig = { 0 };
2379#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002380static ConsoleBuffer g_cbNonTermcap = { 0 };
2381static ConsoleBuffer g_cbTermcap = { 0 };
2382
2383#ifdef FEAT_TITLE
2384#ifdef __BORLANDC__
2385typedef HWND (__stdcall *GETCONSOLEWINDOWPROC)(VOID);
2386#else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002387typedef HWND (WINAPI *GETCONSOLEWINDOWPROC)(VOID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002388#endif
2389char g_szOrigTitle[256] = { 0 };
2390HWND g_hWnd = NULL; /* also used in os_mswin.c */
2391static HICON g_hOrigIconSmall = NULL;
2392static HICON g_hOrigIcon = NULL;
2393static HICON g_hVimIcon = NULL;
2394static BOOL g_fCanChangeIcon = FALSE;
2395
2396/* ICON* are not defined in VC++ 4.0 */
2397#ifndef ICON_SMALL
2398#define ICON_SMALL 0
2399#endif
2400#ifndef ICON_BIG
2401#define ICON_BIG 1
2402#endif
2403/*
2404 * GetConsoleIcon()
2405 * Description:
2406 * Attempts to retrieve the small icon and/or the big icon currently in
2407 * use by a given window.
2408 * Returns:
2409 * TRUE on success
2410 */
2411 static BOOL
2412GetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002413 HWND hWnd,
2414 HICON *phIconSmall,
2415 HICON *phIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002416{
2417 if (hWnd == NULL)
2418 return FALSE;
2419
2420 if (phIconSmall != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002421 *phIconSmall = (HICON)SendMessage(hWnd, WM_GETICON,
2422 (WPARAM)ICON_SMALL, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002423 if (phIcon != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002424 *phIcon = (HICON)SendMessage(hWnd, WM_GETICON,
2425 (WPARAM)ICON_BIG, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002426 return TRUE;
2427}
2428
2429/*
2430 * SetConsoleIcon()
2431 * Description:
2432 * Attempts to change the small icon and/or the big icon currently in
2433 * use by a given window.
2434 * Returns:
2435 * TRUE on success
2436 */
2437 static BOOL
2438SetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002439 HWND hWnd,
2440 HICON hIconSmall,
2441 HICON hIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002442{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443 if (hWnd == NULL)
2444 return FALSE;
2445
2446 if (hIconSmall != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002447 SendMessage(hWnd, WM_SETICON,
2448 (WPARAM)ICON_SMALL, (LPARAM)hIconSmall);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002449 if (hIcon != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002450 SendMessage(hWnd, WM_SETICON,
2451 (WPARAM)ICON_BIG, (LPARAM) hIcon);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452 return TRUE;
2453}
2454
2455/*
2456 * SaveConsoleTitleAndIcon()
2457 * Description:
2458 * Saves the current console window title in g_szOrigTitle, for later
2459 * restoration. Also, attempts to obtain a handle to the console window,
2460 * and use it to save the small and big icons currently in use by the
2461 * console window. This is not always possible on some versions of Windows;
2462 * nor is it possible when running Vim remotely using Telnet (since the
2463 * console window the user sees is owned by a remote process).
2464 */
2465 static void
2466SaveConsoleTitleAndIcon(void)
2467{
2468 GETCONSOLEWINDOWPROC GetConsoleWindowProc;
2469
2470 /* Save the original title. */
2471 if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle)))
2472 return;
2473
2474 /*
2475 * Obtain a handle to the console window using GetConsoleWindow() from
2476 * KERNEL32.DLL; we need to handle in order to change the window icon.
2477 * This function only exists on NT-based Windows, starting with Windows
2478 * 2000. On older operating systems, we can't change the window icon
2479 * anyway.
2480 */
2481 if ((GetConsoleWindowProc = (GETCONSOLEWINDOWPROC)
2482 GetProcAddress(GetModuleHandle("KERNEL32.DLL"),
2483 "GetConsoleWindow")) != NULL)
2484 {
2485 g_hWnd = (*GetConsoleWindowProc)();
2486 }
2487 if (g_hWnd == NULL)
2488 return;
2489
2490 /* Save the original console window icon. */
2491 GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon);
2492 if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL)
2493 return;
2494
2495 /* Extract the first icon contained in the Vim executable. */
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02002496 if (mch_icon_load((HANDLE *)&g_hVimIcon) == FAIL || g_hVimIcon == NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002497 g_hVimIcon = ExtractIcon(NULL, (LPCSTR)exe_name, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002498 if (g_hVimIcon != NULL)
2499 g_fCanChangeIcon = TRUE;
2500}
2501#endif
2502
2503static int g_fWindInitCalled = FALSE;
2504static int g_fTermcapMode = FALSE;
2505static CONSOLE_CURSOR_INFO g_cci;
2506static DWORD g_cmodein = 0;
2507static DWORD g_cmodeout = 0;
2508
2509/*
2510 * non-GUI version of mch_init().
2511 */
2512 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002513mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002514{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002515#ifndef FEAT_RESTORE_ORIG_SCREEN
2516 CONSOLE_SCREEN_BUFFER_INFO csbi;
2517#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002518#ifndef __MINGW32__
2519 extern int _fmode;
2520#endif
2521
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002522 /* Silently handle invalid parameters to CRT functions */
2523 SET_INVALID_PARAM_HANDLER;
2524
Bram Moolenaar071d4272004-06-13 20:20:40 +00002525 /* Let critical errors result in a failure, not in a dialog box. Required
2526 * for the timestamp test to work on removed floppies. */
2527 SetErrorMode(SEM_FAILCRITICALERRORS);
2528
2529 _fmode = O_BINARY; /* we do our own CR-LF translation */
2530 out_flush();
2531
2532 /* Obtain handles for the standard Console I/O devices */
2533 if (read_cmd_fd == 0)
2534 g_hConIn = GetStdHandle(STD_INPUT_HANDLE);
2535 else
2536 create_conin();
2537 g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
2538
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002539#ifdef FEAT_RESTORE_ORIG_SCREEN
2540 /* Save the initial console buffer for later restoration */
2541 SaveConsoleBuffer(&g_cbOrig);
2542 g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
2543#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002544 /* Get current text attributes */
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002545 GetConsoleScreenBufferInfo(g_hConOut, &csbi);
2546 g_attrCurrent = g_attrDefault = csbi.wAttributes;
2547#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002548 if (cterm_normal_fg_color == 0)
2549 cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
2550 if (cterm_normal_bg_color == 0)
2551 cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
2552
2553 /* set termcap codes to current text attributes */
2554 update_tcap(g_attrCurrent);
2555
2556 GetConsoleCursorInfo(g_hConOut, &g_cci);
2557 GetConsoleMode(g_hConIn, &g_cmodein);
2558 GetConsoleMode(g_hConOut, &g_cmodeout);
2559
2560#ifdef FEAT_TITLE
2561 SaveConsoleTitleAndIcon();
2562 /*
2563 * Set both the small and big icons of the console window to Vim's icon.
2564 * Note that Vim presently only has one size of icon (32x32), but it
2565 * automatically gets scaled down to 16x16 when setting the small icon.
2566 */
2567 if (g_fCanChangeIcon)
2568 SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon);
2569#endif
2570
2571 ui_get_shellsize();
2572
2573#ifdef MCH_WRITE_DUMP
2574 fdDump = fopen("dump", "wt");
2575
2576 if (fdDump)
2577 {
2578 time_t t;
2579
2580 time(&t);
2581 fputs(ctime(&t), fdDump);
2582 fflush(fdDump);
2583 }
2584#endif
2585
2586 g_fWindInitCalled = TRUE;
2587
2588#ifdef FEAT_MOUSE
2589 g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
2590#endif
2591
2592#ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002593 win_clip_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002594#endif
2595
2596 /* This will be NULL on anything but NT 4.0 */
2597 s_pfnGetConsoleKeyboardLayoutName =
2598 (PFNGCKLN) GetProcAddress(GetModuleHandle("kernel32.dll"),
2599 "GetConsoleKeyboardLayoutNameA");
2600}
2601
2602/*
2603 * non-GUI version of mch_exit().
2604 * Shut down and exit with status `r'
2605 * Careful: mch_exit() may be called before mch_init()!
2606 */
2607 void
2608mch_exit(int r)
2609{
2610 stoptermcap();
2611
2612 if (g_fWindInitCalled)
2613 settmode(TMODE_COOK);
2614
2615 ml_close_all(TRUE); /* remove all memfiles */
2616
2617 if (g_fWindInitCalled)
2618 {
2619#ifdef FEAT_TITLE
2620 mch_restore_title(3);
2621 /*
2622 * Restore both the small and big icons of the console window to
2623 * what they were at startup. Don't do this when the window is
2624 * closed, Vim would hang here.
2625 */
2626 if (g_fCanChangeIcon && !g_fForceExit)
2627 SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon);
2628#endif
2629
2630#ifdef MCH_WRITE_DUMP
2631 if (fdDump)
2632 {
2633 time_t t;
2634
2635 time(&t);
2636 fputs(ctime(&t), fdDump);
2637 fclose(fdDump);
2638 }
2639 fdDump = NULL;
2640#endif
2641 }
2642
2643 SetConsoleCursorInfo(g_hConOut, &g_cci);
2644 SetConsoleMode(g_hConIn, g_cmodein);
2645 SetConsoleMode(g_hConOut, g_cmodeout);
2646
2647#ifdef DYNAMIC_GETTEXT
2648 dyn_libintl_end();
2649#endif
2650
2651 exit(r);
2652}
2653#endif /* !FEAT_GUI_W32 */
2654
Bram Moolenaar071d4272004-06-13 20:20:40 +00002655/*
2656 * Do we have an interactive window?
2657 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00002658/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00002659 int
2660mch_check_win(
2661 int argc,
2662 char **argv)
2663{
2664 get_exe_name();
2665
2666#ifdef FEAT_GUI_W32
2667 return OK; /* GUI always has a tty */
2668#else
2669 if (isatty(1))
2670 return OK;
2671 return FAIL;
2672#endif
2673}
2674
2675
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002676#ifdef FEAT_MBYTE
2677/*
2678 * fname_casew(): Wide version of fname_case(). Set the case of the file name,
2679 * if it already exists. When "len" is > 0, also expand short to long
2680 * filenames.
2681 * Return FAIL if wide functions are not available, OK otherwise.
2682 * NOTE: much of this is identical to fname_case(), keep in sync!
2683 */
2684 static int
2685fname_casew(
2686 WCHAR *name,
2687 int len)
2688{
2689 WCHAR szTrueName[_MAX_PATH + 2];
2690 WCHAR szTrueNameTemp[_MAX_PATH + 2];
2691 WCHAR *ptrue, *ptruePrev;
2692 WCHAR *porig, *porigPrev;
2693 int flen;
2694 WIN32_FIND_DATAW fb;
Bram Moolenaar73c61632013-12-07 14:48:10 +01002695 HANDLE hFind = INVALID_HANDLE_VALUE;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002696 int c;
2697 int slen;
2698
2699 flen = (int)wcslen(name);
2700 if (flen > _MAX_PATH)
2701 return OK;
2702
2703 /* slash_adjust(name) not needed, already adjusted by fname_case(). */
2704
2705 /* Build the new name in szTrueName[] one component at a time. */
2706 porig = name;
2707 ptrue = szTrueName;
2708
2709 if (iswalpha(porig[0]) && porig[1] == L':')
2710 {
2711 /* copy leading drive letter */
2712 *ptrue++ = *porig++;
2713 *ptrue++ = *porig++;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002714 }
Bram Moolenaar73c61632013-12-07 14:48:10 +01002715 *ptrue = NUL; /* in case nothing follows */
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002716
2717 while (*porig != NUL)
2718 {
2719 /* copy \ characters */
2720 while (*porig == psepc)
2721 *ptrue++ = *porig++;
2722
2723 ptruePrev = ptrue;
2724 porigPrev = porig;
2725 while (*porig != NUL && *porig != psepc)
2726 {
2727 *ptrue++ = *porig++;
2728 }
2729 *ptrue = NUL;
2730
2731 /* To avoid a slow failure append "\*" when searching a directory,
2732 * server or network share. */
2733 wcscpy(szTrueNameTemp, szTrueName);
2734 slen = (int)wcslen(szTrueNameTemp);
2735 if (*porig == psepc && slen + 2 < _MAX_PATH)
2736 wcscpy(szTrueNameTemp + slen, L"\\*");
2737
2738 /* Skip "", "." and "..". */
2739 if (ptrue > ptruePrev
2740 && (ptruePrev[0] != L'.'
2741 || (ptruePrev[1] != NUL
2742 && (ptruePrev[1] != L'.' || ptruePrev[2] != NUL)))
2743 && (hFind = FindFirstFileW(szTrueNameTemp, &fb))
2744 != INVALID_HANDLE_VALUE)
2745 {
2746 c = *porig;
2747 *porig = NUL;
2748
2749 /* Only use the match when it's the same name (ignoring case) or
2750 * expansion is allowed and there is a match with the short name
2751 * and there is enough room. */
2752 if (_wcsicoll(porigPrev, fb.cFileName) == 0
2753 || (len > 0
2754 && (_wcsicoll(porigPrev, fb.cAlternateFileName) == 0
2755 && (int)(ptruePrev - szTrueName)
2756 + (int)wcslen(fb.cFileName) < len)))
2757 {
2758 wcscpy(ptruePrev, fb.cFileName);
2759
2760 /* Look for exact match and prefer it if found. Must be a
2761 * long name, otherwise there would be only one match. */
2762 while (FindNextFileW(hFind, &fb))
2763 {
2764 if (*fb.cAlternateFileName != NUL
2765 && (wcscoll(porigPrev, fb.cFileName) == 0
2766 || (len > 0
2767 && (_wcsicoll(porigPrev,
2768 fb.cAlternateFileName) == 0
2769 && (int)(ptruePrev - szTrueName)
2770 + (int)wcslen(fb.cFileName) < len))))
2771 {
2772 wcscpy(ptruePrev, fb.cFileName);
2773 break;
2774 }
2775 }
2776 }
2777 FindClose(hFind);
2778 *porig = c;
2779 ptrue = ptruePrev + wcslen(ptruePrev);
2780 }
2781 else if (hFind == INVALID_HANDLE_VALUE
2782 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2783 return FAIL;
2784 }
2785
2786 wcscpy(name, szTrueName);
2787 return OK;
2788}
2789#endif
2790
Bram Moolenaar071d4272004-06-13 20:20:40 +00002791/*
2792 * fname_case(): Set the case of the file name, if it already exists.
2793 * When "len" is > 0, also expand short to long filenames.
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002794 * NOTE: much of this is identical to fname_casew(), keep in sync!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002795 */
2796 void
2797fname_case(
2798 char_u *name,
2799 int len)
2800{
2801 char szTrueName[_MAX_PATH + 2];
Bram Moolenaar464c9252010-10-13 20:37:41 +02002802 char szTrueNameTemp[_MAX_PATH + 2];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002803 char *ptrue, *ptruePrev;
2804 char *porig, *porigPrev;
2805 int flen;
2806 WIN32_FIND_DATA fb;
2807 HANDLE hFind;
2808 int c;
Bram Moolenaar464c9252010-10-13 20:37:41 +02002809 int slen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002810
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +00002811 flen = (int)STRLEN(name);
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002812 if (flen == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813 return;
2814
2815 slash_adjust(name);
2816
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002817#ifdef FEAT_MBYTE
2818 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2819 {
2820 WCHAR *p = enc_to_utf16(name, NULL);
2821
2822 if (p != NULL)
2823 {
2824 char_u *q;
Bram Moolenaar21d89b62014-10-07 10:38:40 +02002825 WCHAR buf[_MAX_PATH + 1];
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002826
Bram Moolenaar21d89b62014-10-07 10:38:40 +02002827 wcsncpy(buf, p, _MAX_PATH);
2828 buf[_MAX_PATH] = L'\0';
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002829 vim_free(p);
2830
2831 if (fname_casew(buf, (len > 0) ? _MAX_PATH : 0) == OK)
2832 {
2833 q = utf16_to_enc(buf, NULL);
2834 if (q != NULL)
2835 {
2836 vim_strncpy(name, q, (len > 0) ? len - 1 : flen);
2837 vim_free(q);
2838 return;
2839 }
2840 }
2841 }
2842 /* Retry with non-wide function (for Windows 98). */
2843 }
2844#endif
2845
2846 /* If 'enc' is utf-8, flen can be larger than _MAX_PATH.
2847 * So we should check this after calling wide function. */
2848 if (flen > _MAX_PATH)
2849 return;
2850
Bram Moolenaar071d4272004-06-13 20:20:40 +00002851 /* Build the new name in szTrueName[] one component at a time. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002852 porig = (char *)name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002853 ptrue = szTrueName;
2854
2855 if (isalpha(porig[0]) && porig[1] == ':')
2856 {
2857 /* copy leading drive letter */
2858 *ptrue++ = *porig++;
2859 *ptrue++ = *porig++;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002860 }
Bram Moolenaar73c61632013-12-07 14:48:10 +01002861 *ptrue = NUL; /* in case nothing follows */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002862
2863 while (*porig != NUL)
2864 {
2865 /* copy \ characters */
2866 while (*porig == psepc)
2867 *ptrue++ = *porig++;
2868
2869 ptruePrev = ptrue;
2870 porigPrev = porig;
2871 while (*porig != NUL && *porig != psepc)
2872 {
2873#ifdef FEAT_MBYTE
2874 int l;
2875
2876 if (enc_dbcs)
2877 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002878 l = (*mb_ptr2len)((char_u *)porig);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002879 while (--l >= 0)
2880 *ptrue++ = *porig++;
2881 }
2882 else
2883#endif
2884 *ptrue++ = *porig++;
2885 }
2886 *ptrue = NUL;
2887
Bram Moolenaar464c9252010-10-13 20:37:41 +02002888 /* To avoid a slow failure append "\*" when searching a directory,
2889 * server or network share. */
2890 STRCPY(szTrueNameTemp, szTrueName);
Bram Moolenaar6b5ef062010-10-27 12:18:00 +02002891 slen = (int)strlen(szTrueNameTemp);
Bram Moolenaar464c9252010-10-13 20:37:41 +02002892 if (*porig == psepc && slen + 2 < _MAX_PATH)
2893 STRCPY(szTrueNameTemp + slen, "\\*");
2894
Bram Moolenaar071d4272004-06-13 20:20:40 +00002895 /* Skip "", "." and "..". */
2896 if (ptrue > ptruePrev
2897 && (ptruePrev[0] != '.'
2898 || (ptruePrev[1] != NUL
2899 && (ptruePrev[1] != '.' || ptruePrev[2] != NUL)))
Bram Moolenaar464c9252010-10-13 20:37:41 +02002900 && (hFind = FindFirstFile(szTrueNameTemp, &fb))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002901 != INVALID_HANDLE_VALUE)
2902 {
2903 c = *porig;
2904 *porig = NUL;
2905
2906 /* Only use the match when it's the same name (ignoring case) or
2907 * expansion is allowed and there is a match with the short name
2908 * and there is enough room. */
2909 if (_stricoll(porigPrev, fb.cFileName) == 0
2910 || (len > 0
2911 && (_stricoll(porigPrev, fb.cAlternateFileName) == 0
2912 && (int)(ptruePrev - szTrueName)
2913 + (int)strlen(fb.cFileName) < len)))
2914 {
2915 STRCPY(ptruePrev, fb.cFileName);
2916
2917 /* Look for exact match and prefer it if found. Must be a
2918 * long name, otherwise there would be only one match. */
2919 while (FindNextFile(hFind, &fb))
2920 {
2921 if (*fb.cAlternateFileName != NUL
2922 && (strcoll(porigPrev, fb.cFileName) == 0
2923 || (len > 0
2924 && (_stricoll(porigPrev,
2925 fb.cAlternateFileName) == 0
2926 && (int)(ptruePrev - szTrueName)
2927 + (int)strlen(fb.cFileName) < len))))
2928 {
2929 STRCPY(ptruePrev, fb.cFileName);
2930 break;
2931 }
2932 }
2933 }
2934 FindClose(hFind);
2935 *porig = c;
2936 ptrue = ptruePrev + strlen(ptruePrev);
2937 }
2938 }
2939
2940 STRCPY(name, szTrueName);
2941}
2942
2943
2944/*
2945 * Insert user name in s[len].
2946 */
2947 int
2948mch_get_user_name(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002949 char_u *s,
2950 int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002951{
Bram Moolenaar41a09032007-10-01 18:34:34 +00002952 char szUserName[256 + 1]; /* UNLEN is 256 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002953 DWORD cch = sizeof szUserName;
2954
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01002955#ifdef FEAT_MBYTE
2956 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2957 {
2958 WCHAR wszUserName[256 + 1]; /* UNLEN is 256 */
2959 DWORD wcch = sizeof(wszUserName) / sizeof(WCHAR);
2960
2961 if (GetUserNameW(wszUserName, &wcch))
2962 {
2963 char_u *p = utf16_to_enc(wszUserName, NULL);
2964
2965 if (p != NULL)
2966 {
2967 vim_strncpy(s, p, len - 1);
2968 vim_free(p);
2969 return OK;
2970 }
2971 }
Bram Moolenaarcd981f22014-02-11 17:06:00 +01002972 else if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
2973 return FAIL;
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01002974 /* Retry with non-wide function (for Windows 98). */
2975 }
2976#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002977 if (GetUserName(szUserName, &cch))
2978 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002979 vim_strncpy(s, (char_u *)szUserName, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002980 return OK;
2981 }
2982 s[0] = NUL;
2983 return FAIL;
2984}
2985
2986
2987/*
2988 * Insert host name in s[len].
2989 */
2990 void
2991mch_get_host_name(
2992 char_u *s,
2993 int len)
2994{
2995 DWORD cch = len;
2996
Bram Moolenaar2cc87382013-12-11 18:21:45 +01002997#ifdef FEAT_MBYTE
2998 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2999 {
3000 WCHAR wszHostName[256 + 1];
3001 DWORD wcch = sizeof(wszHostName) / sizeof(WCHAR);
3002
3003 if (GetComputerNameW(wszHostName, &wcch))
3004 {
3005 char_u *p = utf16_to_enc(wszHostName, NULL);
3006
3007 if (p != NULL)
3008 {
3009 vim_strncpy(s, p, len - 1);
3010 vim_free(p);
3011 return;
3012 }
3013 }
Bram Moolenaarcd981f22014-02-11 17:06:00 +01003014 else if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3015 return;
Bram Moolenaar2cc87382013-12-11 18:21:45 +01003016 /* Retry with non-wide function (for Windows 98). */
3017 }
3018#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003019 if (!GetComputerName((LPSTR)s, &cch))
3020 vim_strncpy(s, (char_u *)"PC (Win32 Vim)", len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003021}
3022
3023
3024/*
3025 * return process ID
3026 */
3027 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003028mch_get_pid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003029{
3030 return (long)GetCurrentProcessId();
3031}
3032
3033
3034/*
3035 * Get name of current directory into buffer 'buf' of length 'len' bytes.
3036 * Return OK for success, FAIL for failure.
3037 */
3038 int
3039mch_dirname(
3040 char_u *buf,
3041 int len)
3042{
3043 /*
3044 * Originally this was:
3045 * return (getcwd(buf, len) != NULL ? OK : FAIL);
3046 * But the Win32s known bug list says that getcwd() doesn't work
3047 * so use the Win32 system call instead. <Negri>
3048 */
3049#ifdef FEAT_MBYTE
3050 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3051 {
3052 WCHAR wbuf[_MAX_PATH + 1];
3053
3054 if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0)
3055 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00003056 char_u *p = utf16_to_enc(wbuf, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003057
3058 if (p != NULL)
3059 {
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00003060 vim_strncpy(buf, p, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003061 vim_free(p);
3062 return OK;
3063 }
3064 }
Bram Moolenaarcd981f22014-02-11 17:06:00 +01003065 else if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3066 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067 /* Retry with non-wide function (for Windows 98). */
3068 }
3069#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003070 return (GetCurrentDirectory(len, (LPSTR)buf) != 0 ? OK : FAIL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003071}
3072
3073/*
Bram Moolenaarffa22202013-11-21 12:34:11 +01003074 * Get file permissions for "name".
3075 * Return mode_t or -1 for error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003076 */
3077 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003078mch_getperm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003079{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003080 struct stat st;
Bram Moolenaarffa22202013-11-21 12:34:11 +01003081 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003082
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003083 n = mch_stat((char *)name, &st);
Bram Moolenaar78cf3f02014-01-10 18:16:07 +01003084 return n == 0 ? (long)(unsigned short)st.st_mode : -1L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003085}
3086
3087
3088/*
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003089 * Set file permission for "name" to "perm".
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003090 *
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003091 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003092 */
3093 int
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003094mch_setperm(char_u *name, long perm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003095{
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003096 long n = -1;
3097
Bram Moolenaar071d4272004-06-13 20:20:40 +00003098#ifdef FEAT_MBYTE
3099 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3100 {
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003101 WCHAR *p = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003102
3103 if (p != NULL)
3104 {
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003105 n = _wchmod(p, perm);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003106 vim_free(p);
Bram Moolenaarcd981f22014-02-11 17:06:00 +01003107 if (n == -1 && g_PlatformId == VER_PLATFORM_WIN32_NT)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003108 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003109 /* Retry with non-wide function (for Windows 98). */
3110 }
3111 }
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003112 if (n == -1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003113#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003114 n = _chmod((const char *)name, perm);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003115 if (n == -1)
3116 return FAIL;
3117
3118 win32_set_archive(name);
3119
3120 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003121}
3122
3123/*
3124 * Set hidden flag for "name".
3125 */
3126 void
3127mch_hide(char_u *name)
3128{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003129 int attrs = win32_getattrs(name);
3130 if (attrs == -1)
3131 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003132
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003133 attrs |= FILE_ATTRIBUTE_HIDDEN;
3134 win32_setattrs(name, attrs);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003135}
3136
3137/*
Bram Moolenaar8a52ba72015-11-02 14:45:56 +01003138 * Return TRUE if file "name" exists and is hidden.
3139 */
3140 int
3141mch_ishidden(char_u *name)
3142{
3143 int f = win32_getattrs(name);
3144
3145 if (f == -1)
3146 return FALSE; /* file does not exist at all */
3147
3148 return (f & FILE_ATTRIBUTE_HIDDEN) != 0;
3149}
3150
3151/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003152 * return TRUE if "name" is a directory
3153 * return FALSE if "name" is not a directory or upon error
3154 */
3155 int
3156mch_isdir(char_u *name)
3157{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003158 int f = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003159
3160 if (f == -1)
3161 return FALSE; /* file does not exist at all */
3162
3163 return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
3164}
3165
3166/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003167 * return TRUE if "name" is a directory, NOT a symlink to a directory
3168 * return FALSE if "name" is not a directory
3169 * return FALSE for error
3170 */
3171 int
3172mch_isrealdir(char_u *name)
3173{
3174 return mch_isdir(name) && !mch_is_symbolic_link(name);
3175}
3176
3177/*
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003178 * Create directory "name".
3179 * Return 0 on success, -1 on error.
3180 */
3181 int
3182mch_mkdir(char_u *name)
3183{
3184#ifdef FEAT_MBYTE
3185 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3186 {
3187 WCHAR *p;
3188 int retval;
3189
3190 p = enc_to_utf16(name, NULL);
3191 if (p == NULL)
3192 return -1;
3193 retval = _wmkdir(p);
3194 vim_free(p);
3195 return retval;
3196 }
3197#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003198 return _mkdir((const char *)name);
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003199}
3200
3201/*
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003202 * Delete directory "name".
3203 * Return 0 on success, -1 on error.
3204 */
3205 int
3206mch_rmdir(char_u *name)
3207{
3208#ifdef FEAT_MBYTE
3209 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3210 {
3211 WCHAR *p;
3212 int retval;
3213
3214 p = enc_to_utf16(name, NULL);
3215 if (p == NULL)
3216 return -1;
3217 retval = _wrmdir(p);
3218 vim_free(p);
3219 return retval;
3220 }
3221#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003222 return _rmdir((const char *)name);
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003223}
3224
3225/*
Bram Moolenaar03f48552006-02-28 23:52:23 +00003226 * Return TRUE if file "fname" has more than one link.
3227 */
3228 int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003229mch_is_hard_link(char_u *fname)
Bram Moolenaar03f48552006-02-28 23:52:23 +00003230{
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003231 BY_HANDLE_FILE_INFORMATION info;
3232
3233 return win32_fileinfo(fname, &info) == FILEINFO_OK
3234 && info.nNumberOfLinks > 1;
3235}
3236
3237/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003238 * Return TRUE if "name" is a symbolic link (or a junction).
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003239 */
3240 int
Bram Moolenaar203258c2016-01-17 22:15:16 +01003241mch_is_symbolic_link(char_u *name)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003242{
3243 HANDLE hFind;
3244 int res = FALSE;
3245 WIN32_FIND_DATAA findDataA;
3246 DWORD fileFlags = 0, reparseTag = 0;
3247#ifdef FEAT_MBYTE
3248 WCHAR *wn = NULL;
3249 WIN32_FIND_DATAW findDataW;
3250
3251 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar203258c2016-01-17 22:15:16 +01003252 wn = enc_to_utf16(name, NULL);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003253 if (wn != NULL)
3254 {
3255 hFind = FindFirstFileW(wn, &findDataW);
3256 vim_free(wn);
3257 if (hFind == INVALID_HANDLE_VALUE
3258 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3259 {
3260 /* Retry with non-wide function (for Windows 98). */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003261 hFind = FindFirstFile((LPCSTR)name, &findDataA);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003262 if (hFind != INVALID_HANDLE_VALUE)
3263 {
3264 fileFlags = findDataA.dwFileAttributes;
3265 reparseTag = findDataA.dwReserved0;
3266 }
3267 }
3268 else
3269 {
3270 fileFlags = findDataW.dwFileAttributes;
3271 reparseTag = findDataW.dwReserved0;
3272 }
3273 }
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003274 else
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003275#endif
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003276 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003277 hFind = FindFirstFile((LPCSTR)name, &findDataA);
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003278 if (hFind != INVALID_HANDLE_VALUE)
3279 {
3280 fileFlags = findDataA.dwFileAttributes;
3281 reparseTag = findDataA.dwReserved0;
3282 }
3283 }
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003284
3285 if (hFind != INVALID_HANDLE_VALUE)
3286 FindClose(hFind);
3287
3288 if ((fileFlags & FILE_ATTRIBUTE_REPARSE_POINT)
Bram Moolenaar203258c2016-01-17 22:15:16 +01003289 && (reparseTag == IO_REPARSE_TAG_SYMLINK
3290 || reparseTag == IO_REPARSE_TAG_MOUNT_POINT))
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003291 res = TRUE;
3292
3293 return res;
3294}
3295
3296/*
3297 * Return TRUE if file "fname" has more than one link or if it is a symbolic
3298 * link.
3299 */
3300 int
3301mch_is_linked(char_u *fname)
3302{
3303 if (mch_is_hard_link(fname) || mch_is_symbolic_link(fname))
3304 return TRUE;
3305 return FALSE;
3306}
3307
3308/*
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003309 * Get the by-handle-file-information for "fname".
3310 * Returns FILEINFO_OK when OK.
3311 * returns FILEINFO_ENC_FAIL when enc_to_utf16() failed.
3312 * Returns FILEINFO_READ_FAIL when CreateFile() failed.
3313 * Returns FILEINFO_INFO_FAIL when GetFileInformationByHandle() failed.
3314 */
3315 int
3316win32_fileinfo(char_u *fname, BY_HANDLE_FILE_INFORMATION *info)
3317{
Bram Moolenaar03f48552006-02-28 23:52:23 +00003318 HANDLE hFile;
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003319 int res = FILEINFO_READ_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003320#ifdef FEAT_MBYTE
3321 WCHAR *wn = NULL;
3322
3323 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003324 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00003325 wn = enc_to_utf16(fname, NULL);
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003326 if (wn == NULL)
3327 res = FILEINFO_ENC_FAIL;
3328 }
Bram Moolenaar03f48552006-02-28 23:52:23 +00003329 if (wn != NULL)
3330 {
3331 hFile = CreateFileW(wn, /* file name */
3332 GENERIC_READ, /* access mode */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003333 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003334 NULL, /* security descriptor */
3335 OPEN_EXISTING, /* creation disposition */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003336 FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003337 NULL); /* handle to template file */
3338 if (hFile == INVALID_HANDLE_VALUE
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003339 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
Bram Moolenaar03f48552006-02-28 23:52:23 +00003340 {
3341 /* Retry with non-wide function (for Windows 98). */
3342 vim_free(wn);
3343 wn = NULL;
3344 }
3345 }
3346 if (wn == NULL)
3347#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003348 hFile = CreateFile((LPCSTR)fname, /* file name */
3349 GENERIC_READ, /* access mode */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003350 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003351 NULL, /* security descriptor */
3352 OPEN_EXISTING, /* creation disposition */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003353 FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003354 NULL); /* handle to template file */
3355
3356 if (hFile != INVALID_HANDLE_VALUE)
3357 {
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003358 if (GetFileInformationByHandle(hFile, info) != 0)
3359 res = FILEINFO_OK;
3360 else
3361 res = FILEINFO_INFO_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003362 CloseHandle(hFile);
3363 }
3364
3365#ifdef FEAT_MBYTE
3366 vim_free(wn);
3367#endif
3368 return res;
3369}
3370
3371/*
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003372 * get file attributes for `name'
3373 * -1 : error
3374 * else FILE_ATTRIBUTE_* defined in winnt.h
3375 */
Bram Moolenaarffa22202013-11-21 12:34:11 +01003376 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003377win32_getattrs(char_u *name)
3378{
3379 int attr;
3380#ifdef FEAT_MBYTE
3381 WCHAR *p = NULL;
3382
3383 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3384 p = enc_to_utf16(name, NULL);
3385
3386 if (p != NULL)
3387 {
3388 attr = GetFileAttributesW(p);
3389 if (attr < 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3390 {
3391 /* Retry with non-wide function (for Windows 98). */
3392 vim_free(p);
3393 p = NULL;
3394 }
3395 }
3396 if (p == NULL)
3397#endif
3398 attr = GetFileAttributes((char *)name);
3399#ifdef FEAT_MBYTE
3400 vim_free(p);
3401#endif
3402 return attr;
3403}
3404
3405/*
3406 * set file attributes for `name' to `attrs'
3407 *
3408 * return -1 for failure, 0 otherwise
3409 */
3410 static
3411 int
3412win32_setattrs(char_u *name, int attrs)
3413{
3414 int res;
3415#ifdef FEAT_MBYTE
3416 WCHAR *p = NULL;
3417
3418 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3419 p = enc_to_utf16(name, NULL);
3420
3421 if (p != NULL)
3422 {
3423 res = SetFileAttributesW(p, attrs);
3424 if (res == FALSE
3425 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3426 {
3427 /* Retry with non-wide function (for Windows 98). */
3428 vim_free(p);
3429 p = NULL;
3430 }
3431 }
3432 if (p == NULL)
3433#endif
3434 res = SetFileAttributes((char *)name, attrs);
3435#ifdef FEAT_MBYTE
3436 vim_free(p);
3437#endif
3438 return res ? 0 : -1;
3439}
3440
3441/*
3442 * Set archive flag for "name".
3443 */
3444 static
3445 int
3446win32_set_archive(char_u *name)
3447{
3448 int attrs = win32_getattrs(name);
3449 if (attrs == -1)
3450 return -1;
3451
3452 attrs |= FILE_ATTRIBUTE_ARCHIVE;
3453 return win32_setattrs(name, attrs);
3454}
3455
3456/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003457 * Return TRUE if file or directory "name" is writable (not readonly).
3458 * Strange semantics of Win32: a readonly directory is writable, but you can't
3459 * delete a file. Let's say this means it is writable.
3460 */
3461 int
3462mch_writable(char_u *name)
3463{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003464 int attrs = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003465
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003466 return (attrs != -1 && (!(attrs & FILE_ATTRIBUTE_READONLY)
3467 || (attrs & FILE_ATTRIBUTE_DIRECTORY)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003468}
3469
Bram Moolenaar071d4272004-06-13 20:20:40 +00003470/*
3471 * Return 1 if "name" can be executed, 0 if not.
Bram Moolenaar77b77102015-03-21 22:18:41 +01003472 * If "use_path" is FALSE only check if "name" is executable.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003473 * Return -1 if unknown.
3474 */
3475 int
Bram Moolenaar77b77102015-03-21 22:18:41 +01003476mch_can_exe(char_u *name, char_u **path, int use_path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003477{
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003478 char_u buf[_MAX_PATH];
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003479 int len = (int)STRLEN(name);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003480 char_u *p;
3481
3482 if (len >= _MAX_PATH) /* safety check */
3483 return FALSE;
Bram Moolenaar77b77102015-03-21 22:18:41 +01003484 if (!use_path)
3485 {
3486 /* TODO: check if file is really executable. */
3487 return mch_getperm(name) != -1 && !mch_isdir(name);
3488 }
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003489
3490 /* If there already is an extension try using the name directly. Also do
3491 * this with a Unix-shell like 'shell'. */
3492 if (vim_strchr(gettail(name), '.') != NULL
3493 || strstr((char *)gettail(p_sh), "sh") != NULL)
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003494 if (executable_exists((char *)name, path))
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003495 return TRUE;
3496
3497 /*
3498 * Loop over all extensions in $PATHEXT.
3499 */
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00003500 vim_strncpy(buf, name, _MAX_PATH - 1);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003501 p = mch_getenv("PATHEXT");
3502 if (p == NULL)
3503 p = (char_u *)".com;.exe;.bat;.cmd";
3504 while (*p)
3505 {
3506 if (p[0] == '.' && (p[1] == NUL || p[1] == ';'))
3507 {
3508 /* A single "." means no extension is added. */
3509 buf[len] = NUL;
3510 ++p;
3511 if (*p)
3512 ++p;
3513 }
3514 else
3515 copy_option_part(&p, buf + len, _MAX_PATH - len, ";");
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003516 if (executable_exists((char *)buf, path))
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003517 return TRUE;
3518 }
3519 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003520}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003521
3522/*
3523 * Check what "name" is:
3524 * NODE_NORMAL: file or directory (or doesn't exist)
3525 * NODE_WRITABLE: writable device, socket, fifo, etc.
3526 * NODE_OTHER: non-writable things
3527 */
3528 int
3529mch_nodetype(char_u *name)
3530{
3531 HANDLE hFile;
3532 int type;
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003533#ifdef FEAT_MBYTE
3534 WCHAR *wn = NULL;
3535#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003536
Bram Moolenaar043545e2006-10-10 16:44:07 +00003537 /* We can't open a file with a name "\\.\con" or "\\.\prn" and trying to
3538 * read from it later will cause Vim to hang. Thus return NODE_WRITABLE
3539 * here. */
3540 if (STRNCMP(name, "\\\\.\\", 4) == 0)
3541 return NODE_WRITABLE;
3542
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003543#ifdef FEAT_MBYTE
3544 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3545 {
3546 wn = enc_to_utf16(name, NULL);
3547 if (wn != NULL)
3548 {
3549 hFile = CreateFileW(wn, /* file name */
3550 GENERIC_WRITE, /* access mode */
3551 0, /* share mode */
3552 NULL, /* security descriptor */
3553 OPEN_EXISTING, /* creation disposition */
3554 0, /* file attributes */
3555 NULL); /* handle to template file */
3556 if (hFile == INVALID_HANDLE_VALUE
3557 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3558 {
3559 /* Retry with non-wide function (for Windows 98). */
3560 vim_free(wn);
3561 wn = NULL;
3562 }
3563 }
3564 }
3565 if (wn == NULL)
3566#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003567 hFile = CreateFile((LPCSTR)name, /* file name */
3568 GENERIC_WRITE, /* access mode */
3569 0, /* share mode */
3570 NULL, /* security descriptor */
3571 OPEN_EXISTING, /* creation disposition */
3572 0, /* file attributes */
3573 NULL); /* handle to template file */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003574
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003575#ifdef FEAT_MBYTE
3576 vim_free(wn);
3577#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003578 if (hFile == INVALID_HANDLE_VALUE)
3579 return NODE_NORMAL;
3580
3581 type = GetFileType(hFile);
3582 CloseHandle(hFile);
3583 if (type == FILE_TYPE_CHAR)
3584 return NODE_WRITABLE;
3585 if (type == FILE_TYPE_DISK)
3586 return NODE_NORMAL;
3587 return NODE_OTHER;
3588}
3589
3590#ifdef HAVE_ACL
3591struct my_acl
3592{
3593 PSECURITY_DESCRIPTOR pSecurityDescriptor;
3594 PSID pSidOwner;
3595 PSID pSidGroup;
3596 PACL pDacl;
3597 PACL pSacl;
3598};
3599#endif
3600
3601/*
3602 * Return a pointer to the ACL of file "fname" in allocated memory.
3603 * Return NULL if the ACL is not available for whatever reason.
3604 */
3605 vim_acl_T
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003606mch_get_acl(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003607{
3608#ifndef HAVE_ACL
3609 return (vim_acl_T)NULL;
3610#else
3611 struct my_acl *p = NULL;
Bram Moolenaar27515922013-06-29 15:36:26 +02003612 DWORD err;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003613
3614 /* This only works on Windows NT and 2000. */
3615 if (g_PlatformId == VER_PLATFORM_WIN32_NT && advapi_lib != NULL)
3616 {
3617 p = (struct my_acl *)alloc_clear((unsigned)sizeof(struct my_acl));
3618 if (p != NULL)
3619 {
Bram Moolenaar27515922013-06-29 15:36:26 +02003620# ifdef FEAT_MBYTE
3621 WCHAR *wn = NULL;
3622
3623 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3624 wn = enc_to_utf16(fname, NULL);
3625 if (wn != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003626 {
Bram Moolenaar27515922013-06-29 15:36:26 +02003627 /* Try to retrieve the entire security descriptor. */
3628 err = pGetNamedSecurityInfoW(
3629 wn, // Abstract filename
3630 SE_FILE_OBJECT, // File Object
3631 OWNER_SECURITY_INFORMATION |
3632 GROUP_SECURITY_INFORMATION |
3633 DACL_SECURITY_INFORMATION |
3634 SACL_SECURITY_INFORMATION,
3635 &p->pSidOwner, // Ownership information.
3636 &p->pSidGroup, // Group membership.
3637 &p->pDacl, // Discretionary information.
3638 &p->pSacl, // For auditing purposes.
3639 &p->pSecurityDescriptor);
3640 if (err == ERROR_ACCESS_DENIED ||
3641 err == ERROR_PRIVILEGE_NOT_HELD)
3642 {
3643 /* Retrieve only DACL. */
3644 (void)pGetNamedSecurityInfoW(
3645 wn,
3646 SE_FILE_OBJECT,
3647 DACL_SECURITY_INFORMATION,
3648 NULL,
3649 NULL,
3650 &p->pDacl,
3651 NULL,
3652 &p->pSecurityDescriptor);
3653 }
3654 if (p->pSecurityDescriptor == NULL)
3655 {
3656 mch_free_acl((vim_acl_T)p);
3657 p = NULL;
3658 }
3659 vim_free(wn);
3660 }
3661 else
3662# endif
3663 {
3664 /* Try to retrieve the entire security descriptor. */
3665 err = pGetNamedSecurityInfo(
3666 (LPSTR)fname, // Abstract filename
3667 SE_FILE_OBJECT, // File Object
3668 OWNER_SECURITY_INFORMATION |
3669 GROUP_SECURITY_INFORMATION |
3670 DACL_SECURITY_INFORMATION |
3671 SACL_SECURITY_INFORMATION,
3672 &p->pSidOwner, // Ownership information.
3673 &p->pSidGroup, // Group membership.
3674 &p->pDacl, // Discretionary information.
3675 &p->pSacl, // For auditing purposes.
3676 &p->pSecurityDescriptor);
3677 if (err == ERROR_ACCESS_DENIED ||
3678 err == ERROR_PRIVILEGE_NOT_HELD)
3679 {
3680 /* Retrieve only DACL. */
3681 (void)pGetNamedSecurityInfo(
3682 (LPSTR)fname,
3683 SE_FILE_OBJECT,
3684 DACL_SECURITY_INFORMATION,
3685 NULL,
3686 NULL,
3687 &p->pDacl,
3688 NULL,
3689 &p->pSecurityDescriptor);
3690 }
3691 if (p->pSecurityDescriptor == NULL)
3692 {
3693 mch_free_acl((vim_acl_T)p);
3694 p = NULL;
3695 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696 }
3697 }
3698 }
3699
3700 return (vim_acl_T)p;
3701#endif
3702}
3703
Bram Moolenaar27515922013-06-29 15:36:26 +02003704#ifdef HAVE_ACL
3705/*
3706 * Check if "acl" contains inherited ACE.
3707 */
3708 static BOOL
3709is_acl_inherited(PACL acl)
3710{
3711 DWORD i;
3712 ACL_SIZE_INFORMATION acl_info;
3713 PACCESS_ALLOWED_ACE ace;
3714
3715 acl_info.AceCount = 0;
3716 GetAclInformation(acl, &acl_info, sizeof(acl_info), AclSizeInformation);
3717 for (i = 0; i < acl_info.AceCount; i++)
3718 {
3719 GetAce(acl, i, (LPVOID *)&ace);
3720 if (ace->Header.AceFlags & INHERITED_ACE)
3721 return TRUE;
3722 }
3723 return FALSE;
3724}
3725#endif
3726
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727/*
3728 * Set the ACL of file "fname" to "acl" (unless it's NULL).
3729 * Errors are ignored.
3730 * This must only be called with "acl" equal to what mch_get_acl() returned.
3731 */
3732 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003733mch_set_acl(char_u *fname, vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003734{
3735#ifdef HAVE_ACL
3736 struct my_acl *p = (struct my_acl *)acl;
Bram Moolenaar27515922013-06-29 15:36:26 +02003737 SECURITY_INFORMATION sec_info = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003738
3739 if (p != NULL && advapi_lib != NULL)
Bram Moolenaar27515922013-06-29 15:36:26 +02003740 {
3741# ifdef FEAT_MBYTE
3742 WCHAR *wn = NULL;
3743# endif
3744
3745 /* Set security flags */
3746 if (p->pSidOwner)
3747 sec_info |= OWNER_SECURITY_INFORMATION;
3748 if (p->pSidGroup)
3749 sec_info |= GROUP_SECURITY_INFORMATION;
3750 if (p->pDacl)
3751 {
3752 sec_info |= DACL_SECURITY_INFORMATION;
3753 /* Do not inherit its parent's DACL.
3754 * If the DACL is inherited, Cygwin permissions would be changed.
3755 */
3756 if (!is_acl_inherited(p->pDacl))
3757 sec_info |= PROTECTED_DACL_SECURITY_INFORMATION;
3758 }
3759 if (p->pSacl)
3760 sec_info |= SACL_SECURITY_INFORMATION;
3761
3762# ifdef FEAT_MBYTE
3763 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3764 wn = enc_to_utf16(fname, NULL);
3765 if (wn != NULL)
3766 {
3767 (void)pSetNamedSecurityInfoW(
3768 wn, // Abstract filename
3769 SE_FILE_OBJECT, // File Object
3770 sec_info,
3771 p->pSidOwner, // Ownership information.
3772 p->pSidGroup, // Group membership.
3773 p->pDacl, // Discretionary information.
3774 p->pSacl // For auditing purposes.
3775 );
3776 vim_free(wn);
3777 }
3778 else
3779# endif
3780 {
3781 (void)pSetNamedSecurityInfo(
3782 (LPSTR)fname, // Abstract filename
3783 SE_FILE_OBJECT, // File Object
3784 sec_info,
3785 p->pSidOwner, // Ownership information.
3786 p->pSidGroup, // Group membership.
3787 p->pDacl, // Discretionary information.
3788 p->pSacl // For auditing purposes.
3789 );
3790 }
3791 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003792#endif
3793}
3794
3795 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003796mch_free_acl(vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003797{
3798#ifdef HAVE_ACL
3799 struct my_acl *p = (struct my_acl *)acl;
3800
3801 if (p != NULL)
3802 {
3803 LocalFree(p->pSecurityDescriptor); // Free the memory just in case
3804 vim_free(p);
3805 }
3806#endif
3807}
3808
3809#ifndef FEAT_GUI_W32
3810
3811/*
3812 * handler for ctrl-break, ctrl-c interrupts, and fatal events.
3813 */
3814 static BOOL WINAPI
3815handler_routine(
3816 DWORD dwCtrlType)
3817{
3818 switch (dwCtrlType)
3819 {
3820 case CTRL_C_EVENT:
3821 if (ctrl_c_interrupts)
3822 g_fCtrlCPressed = TRUE;
3823 return TRUE;
3824
3825 case CTRL_BREAK_EVENT:
3826 g_fCBrkPressed = TRUE;
3827 return TRUE;
3828
3829 /* fatal events: shut down gracefully */
3830 case CTRL_CLOSE_EVENT:
3831 case CTRL_LOGOFF_EVENT:
3832 case CTRL_SHUTDOWN_EVENT:
3833 windgoto((int)Rows - 1, 0);
3834 g_fForceExit = TRUE;
3835
Bram Moolenaar0fde2902008-03-16 13:54:13 +00003836 vim_snprintf((char *)IObuff, IOSIZE, _("Vim: Caught %s event\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003837 (dwCtrlType == CTRL_CLOSE_EVENT
3838 ? _("close")
3839 : dwCtrlType == CTRL_LOGOFF_EVENT
3840 ? _("logoff")
3841 : _("shutdown")));
3842#ifdef DEBUG
3843 OutputDebugString(IObuff);
3844#endif
3845
3846 preserve_exit(); /* output IObuff, preserve files and exit */
3847
3848 return TRUE; /* not reached */
3849
3850 default:
3851 return FALSE;
3852 }
3853}
3854
3855
3856/*
3857 * set the tty in (raw) ? "raw" : "cooked" mode
3858 */
3859 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003860mch_settmode(int tmode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003861{
3862 DWORD cmodein;
3863 DWORD cmodeout;
3864 BOOL bEnableHandler;
3865
3866 GetConsoleMode(g_hConIn, &cmodein);
3867 GetConsoleMode(g_hConOut, &cmodeout);
3868 if (tmode == TMODE_RAW)
3869 {
3870 cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3871 ENABLE_ECHO_INPUT);
3872#ifdef FEAT_MOUSE
3873 if (g_fMouseActive)
3874 cmodein |= ENABLE_MOUSE_INPUT;
3875#endif
3876 cmodeout &= ~(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3877 bEnableHandler = TRUE;
3878 }
3879 else /* cooked */
3880 {
3881 cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3882 ENABLE_ECHO_INPUT);
3883 cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3884 bEnableHandler = FALSE;
3885 }
3886 SetConsoleMode(g_hConIn, cmodein);
3887 SetConsoleMode(g_hConOut, cmodeout);
3888 SetConsoleCtrlHandler(handler_routine, bEnableHandler);
3889
3890#ifdef MCH_WRITE_DUMP
3891 if (fdDump)
3892 {
3893 fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n",
3894 tmode == TMODE_RAW ? "raw" :
3895 tmode == TMODE_COOK ? "cooked" : "normal",
3896 cmodein, cmodeout);
3897 fflush(fdDump);
3898 }
3899#endif
3900}
3901
3902
3903/*
3904 * Get the size of the current window in `Rows' and `Columns'
3905 * Return OK when size could be determined, FAIL otherwise.
3906 */
3907 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003908mch_get_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003909{
3910 CONSOLE_SCREEN_BUFFER_INFO csbi;
3911
3912 if (!g_fTermcapMode && g_cbTermcap.IsValid)
3913 {
3914 /*
3915 * For some reason, we are trying to get the screen dimensions
3916 * even though we are not in termcap mode. The 'Rows' and 'Columns'
3917 * variables are really intended to mean the size of Vim screen
3918 * while in termcap mode.
3919 */
3920 Rows = g_cbTermcap.Info.dwSize.Y;
3921 Columns = g_cbTermcap.Info.dwSize.X;
3922 }
3923 else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3924 {
3925 Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3926 Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3927 }
3928 else
3929 {
3930 Rows = 25;
3931 Columns = 80;
3932 }
3933 return OK;
3934}
3935
3936/*
3937 * Set a console window to `xSize' * `ySize'
3938 */
3939 static void
3940ResizeConBufAndWindow(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003941 HANDLE hConsole,
3942 int xSize,
3943 int ySize)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003944{
3945 CONSOLE_SCREEN_BUFFER_INFO csbi; /* hold current console buffer info */
3946 SMALL_RECT srWindowRect; /* hold the new console size */
3947 COORD coordScreen;
3948
3949#ifdef MCH_WRITE_DUMP
3950 if (fdDump)
3951 {
3952 fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
3953 fflush(fdDump);
3954 }
3955#endif
3956
3957 /* get the largest size we can size the console window to */
3958 coordScreen = GetLargestConsoleWindowSize(hConsole);
3959
3960 /* define the new console window size and scroll position */
3961 srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
3962 srWindowRect.Right = (SHORT) (min(xSize, coordScreen.X) - 1);
3963 srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
3964
3965 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3966 {
3967 int sx, sy;
3968
3969 sx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3970 sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3971 if (sy < ySize || sx < xSize)
3972 {
3973 /*
3974 * Increasing number of lines/columns, do buffer first.
3975 * Use the maximal size in x and y direction.
3976 */
3977 if (sy < ySize)
3978 coordScreen.Y = ySize;
3979 else
3980 coordScreen.Y = sy;
3981 if (sx < xSize)
3982 coordScreen.X = xSize;
3983 else
3984 coordScreen.X = sx;
3985 SetConsoleScreenBufferSize(hConsole, coordScreen);
3986 }
3987 }
3988
3989 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &srWindowRect))
3990 {
3991#ifdef MCH_WRITE_DUMP
3992 if (fdDump)
3993 {
3994 fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
3995 GetLastError());
3996 fflush(fdDump);
3997 }
3998#endif
3999 }
4000
4001 /* define the new console buffer size */
4002 coordScreen.X = xSize;
4003 coordScreen.Y = ySize;
4004
4005 if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
4006 {
4007#ifdef MCH_WRITE_DUMP
4008 if (fdDump)
4009 {
4010 fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
4011 GetLastError());
4012 fflush(fdDump);
4013 }
4014#endif
4015 }
4016}
4017
4018
4019/*
4020 * Set the console window to `Rows' * `Columns'
4021 */
4022 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004023mch_set_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004024{
4025 COORD coordScreen;
4026
4027 /* Don't change window size while still starting up */
4028 if (suppress_winsize != 0)
4029 {
4030 suppress_winsize = 2;
4031 return;
4032 }
4033
4034 if (term_console)
4035 {
4036 coordScreen = GetLargestConsoleWindowSize(g_hConOut);
4037
4038 /* Clamp Rows and Columns to reasonable values */
4039 if (Rows > coordScreen.Y)
4040 Rows = coordScreen.Y;
4041 if (Columns > coordScreen.X)
4042 Columns = coordScreen.X;
4043
4044 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
4045 }
4046}
4047
4048/*
4049 * Rows and/or Columns has changed.
4050 */
4051 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004052mch_new_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004053{
4054 set_scroll_region(0, 0, Columns - 1, Rows - 1);
4055}
4056
4057
4058/*
4059 * Called when started up, to set the winsize that was delayed.
4060 */
4061 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004062mch_set_winsize_now(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004063{
4064 if (suppress_winsize == 2)
4065 {
4066 suppress_winsize = 0;
4067 mch_set_shellsize();
4068 shell_resized();
4069 }
4070 suppress_winsize = 0;
4071}
4072#endif /* FEAT_GUI_W32 */
4073
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004074 static BOOL
4075vim_create_process(
Bram Moolenaar36c85b22013-12-12 20:25:44 +01004076 char *cmd,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004077 BOOL inherit_handles,
Bram Moolenaar3f1138e2014-01-05 13:29:26 +01004078 DWORD flags,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004079 STARTUPINFO *si,
4080 PROCESS_INFORMATION *pi)
4081{
4082# ifdef FEAT_MBYTE
4083 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4084 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004085 WCHAR *wcmd = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004086
4087 if (wcmd != NULL)
4088 {
4089 BOOL ret;
4090 ret = CreateProcessW(
4091 NULL, /* Executable name */
4092 wcmd, /* Command to execute */
4093 NULL, /* Process security attributes */
4094 NULL, /* Thread security attributes */
4095 inherit_handles, /* Inherit handles */
4096 flags, /* Creation flags */
4097 NULL, /* Environment */
4098 NULL, /* Current directory */
Bram Moolenaar36c85b22013-12-12 20:25:44 +01004099 (LPSTARTUPINFOW)si, /* Startup information */
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004100 pi); /* Process information */
4101 vim_free(wcmd);
4102 return ret;
4103 }
4104 }
4105#endif
4106 return CreateProcess(
4107 NULL, /* Executable name */
4108 cmd, /* Command to execute */
4109 NULL, /* Process security attributes */
4110 NULL, /* Thread security attributes */
4111 inherit_handles, /* Inherit handles */
4112 flags, /* Creation flags */
4113 NULL, /* Environment */
4114 NULL, /* Current directory */
4115 si, /* Startup information */
4116 pi); /* Process information */
4117}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004118
4119
4120#if defined(FEAT_GUI_W32) || defined(PROTO)
4121
4122/*
4123 * Specialised version of system() for Win32 GUI mode.
4124 * This version proceeds as follows:
4125 * 1. Create a console window for use by the subprocess
4126 * 2. Run the subprocess (it gets the allocated console by default)
4127 * 3. Wait for the subprocess to terminate and get its exit code
4128 * 4. Prompt the user to press a key to close the console window
4129 */
4130 static int
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004131mch_system_classic(char *cmd, int options)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004132{
4133 STARTUPINFO si;
4134 PROCESS_INFORMATION pi;
4135 DWORD ret = 0;
4136 HWND hwnd = GetFocus();
4137
4138 si.cb = sizeof(si);
4139 si.lpReserved = NULL;
4140 si.lpDesktop = NULL;
4141 si.lpTitle = NULL;
4142 si.dwFlags = STARTF_USESHOWWINDOW;
4143 /*
4144 * It's nicer to run a filter command in a minimized window, but in
4145 * Windows 95 this makes the command MUCH slower. We can't do it under
4146 * Win32s either as it stops the synchronous spawn workaround working.
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004147 * Don't activate the window to keep focus on Vim.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148 */
4149 if ((options & SHELL_DOOUT) && !mch_windows95() && !gui_is_win32s())
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004150 si.wShowWindow = SW_SHOWMINNOACTIVE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004151 else
4152 si.wShowWindow = SW_SHOWNORMAL;
4153 si.cbReserved2 = 0;
4154 si.lpReserved2 = NULL;
4155
Bram Moolenaar942d6b22016-02-07 19:57:16 +01004156 /* There is a strange error on Windows 95 when using "c:\command.com".
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 * When the "c:\\" is left out it works OK...? */
4158 if (mch_windows95()
4159 && (STRNICMP(cmd, "c:/command.com", 14) == 0
4160 || STRNICMP(cmd, "c:\\command.com", 14) == 0))
4161 cmd += 3;
4162
4163 /* Now, run the command */
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004164 vim_create_process(cmd, FALSE,
4165 CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE, &si, &pi);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166
4167 /* Wait for the command to terminate before continuing */
4168 if (g_PlatformId != VER_PLATFORM_WIN32s)
4169 {
4170#ifdef FEAT_GUI
4171 int delay = 1;
4172
4173 /* Keep updating the window while waiting for the shell to finish. */
4174 for (;;)
4175 {
4176 MSG msg;
4177
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02004178 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004179 {
4180 TranslateMessage(&msg);
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02004181 pDispatchMessage(&msg);
Bram Moolenaare4195c52012-08-02 12:31:44 +02004182 delay = 1;
4183 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004184 }
4185 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4186 break;
4187
4188 /* We start waiting for a very short time and then increase it, so
4189 * that we respond quickly when the process is quick, and don't
4190 * consume too much overhead when it's slow. */
4191 if (delay < 50)
4192 delay += 10;
4193 }
4194#else
4195 WaitForSingleObject(pi.hProcess, INFINITE);
4196#endif
4197
4198 /* Get the command exit code */
4199 GetExitCodeProcess(pi.hProcess, &ret);
4200 }
4201 else
4202 {
4203 /*
4204 * This ugly code is the only quick way of performing
4205 * a synchronous spawn under Win32s. Yuk.
4206 */
4207 num_windows = 0;
4208 EnumWindows(win32ssynch_cb, 0);
4209 old_num_windows = num_windows;
4210 do
4211 {
4212 Sleep(1000);
4213 num_windows = 0;
4214 EnumWindows(win32ssynch_cb, 0);
4215 } while (num_windows == old_num_windows);
4216 ret = 0;
4217 }
4218
4219 /* Close the handles to the subprocess, so that it goes away */
4220 CloseHandle(pi.hThread);
4221 CloseHandle(pi.hProcess);
4222
4223 /* Try to get input focus back. Doesn't always work though. */
4224 PostMessage(hwnd, WM_SETFOCUS, 0, 0);
4225
4226 return ret;
4227}
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004228
4229/*
4230 * Thread launched by the gui to send the current buffer data to the
4231 * process. This way avoid to hang up vim totally if the children
4232 * process take a long time to process the lines.
4233 */
4234 static DWORD WINAPI
4235sub_process_writer(LPVOID param)
4236{
4237 HANDLE g_hChildStd_IN_Wr = param;
4238 linenr_T lnum = curbuf->b_op_start.lnum;
4239 DWORD len = 0;
4240 DWORD l;
4241 char_u *lp = ml_get(lnum);
4242 char_u *s;
4243 int written = 0;
4244
4245 for (;;)
4246 {
4247 l = (DWORD)STRLEN(lp + written);
4248 if (l == 0)
4249 len = 0;
4250 else if (lp[written] == NL)
4251 {
4252 /* NL -> NUL translation */
4253 WriteFile(g_hChildStd_IN_Wr, "", 1, &len, NULL);
4254 }
4255 else
4256 {
4257 s = vim_strchr(lp + written, NL);
4258 WriteFile(g_hChildStd_IN_Wr, (char *)lp + written,
4259 s == NULL ? l : (DWORD)(s - (lp + written)),
4260 &len, NULL);
4261 }
4262 if (len == (int)l)
4263 {
4264 /* Finished a line, add a NL, unless this line should not have
4265 * one. */
4266 if (lnum != curbuf->b_op_end.lnum
Bram Moolenaar34d72d42015-07-17 14:18:08 +02004267 || (!curbuf->b_p_bin
4268 && curbuf->b_p_fixeol)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004269 || (lnum != curbuf->b_no_eol_lnum
4270 && (lnum != curbuf->b_ml.ml_line_count
4271 || curbuf->b_p_eol)))
4272 {
Bram Moolenaaraf62ff32013-03-19 14:48:29 +01004273 WriteFile(g_hChildStd_IN_Wr, "\n", 1, (LPDWORD)&ignored, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004274 }
4275
4276 ++lnum;
4277 if (lnum > curbuf->b_op_end.lnum)
4278 break;
4279
4280 lp = ml_get(lnum);
4281 written = 0;
4282 }
4283 else if (len > 0)
4284 written += len;
4285 }
4286
4287 /* finished all the lines, close pipe */
4288 CloseHandle(g_hChildStd_IN_Wr);
4289 ExitThread(0);
4290}
4291
4292
4293# define BUFLEN 100 /* length for buffer, stolen from unix version */
4294
4295/*
4296 * This function read from the children's stdout and write the
4297 * data on screen or in the buffer accordingly.
4298 */
4299 static void
4300dump_pipe(int options,
4301 HANDLE g_hChildStd_OUT_Rd,
4302 garray_T *ga,
4303 char_u buffer[],
4304 DWORD *buffer_off)
4305{
4306 DWORD availableBytes = 0;
4307 DWORD i;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004308 int ret;
4309 DWORD len;
4310 DWORD toRead;
4311 int repeatCount;
4312
4313 /* we query the pipe to see if there is any data to read
4314 * to avoid to perform a blocking read */
4315 ret = PeekNamedPipe(g_hChildStd_OUT_Rd, /* pipe to query */
4316 NULL, /* optional buffer */
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02004317 0, /* buffer size */
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004318 NULL, /* number of read bytes */
4319 &availableBytes, /* available bytes total */
4320 NULL); /* byteLeft */
4321
4322 repeatCount = 0;
4323 /* We got real data in the pipe, read it */
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02004324 while (ret != 0 && availableBytes > 0)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004325 {
4326 repeatCount++;
4327 toRead =
4328# ifdef FEAT_MBYTE
4329 (DWORD)(BUFLEN - *buffer_off);
4330# else
4331 (DWORD)BUFLEN;
4332# endif
4333 toRead = availableBytes < toRead ? availableBytes : toRead;
4334 ReadFile(g_hChildStd_OUT_Rd, buffer
4335# ifdef FEAT_MBYTE
4336 + *buffer_off, toRead
4337# else
4338 , toRead
4339# endif
4340 , &len, NULL);
4341
4342 /* If we haven't read anything, there is a problem */
4343 if (len == 0)
4344 break;
4345
4346 availableBytes -= len;
4347
4348 if (options & SHELL_READ)
4349 {
4350 /* Do NUL -> NL translation, append NL separated
4351 * lines to the current buffer. */
4352 for (i = 0; i < len; ++i)
4353 {
4354 if (buffer[i] == NL)
4355 append_ga_line(ga);
4356 else if (buffer[i] == NUL)
4357 ga_append(ga, NL);
4358 else
4359 ga_append(ga, buffer[i]);
4360 }
4361 }
4362# ifdef FEAT_MBYTE
4363 else if (has_mbyte)
4364 {
4365 int l;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004366 int c;
4367 char_u *p;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004368
4369 len += *buffer_off;
4370 buffer[len] = NUL;
4371
4372 /* Check if the last character in buffer[] is
4373 * incomplete, keep these bytes for the next
4374 * round. */
4375 for (p = buffer; p < buffer + len; p += l)
4376 {
4377 l = mb_cptr2len(p);
4378 if (l == 0)
4379 l = 1; /* NUL byte? */
4380 else if (MB_BYTE2LEN(*p) != l)
4381 break;
4382 }
4383 if (p == buffer) /* no complete character */
4384 {
4385 /* avoid getting stuck at an illegal byte */
4386 if (len >= 12)
4387 ++p;
4388 else
4389 {
4390 *buffer_off = len;
4391 return;
4392 }
4393 }
4394 c = *p;
4395 *p = NUL;
4396 msg_puts(buffer);
4397 if (p < buffer + len)
4398 {
4399 *p = c;
4400 *buffer_off = (DWORD)((buffer + len) - p);
4401 mch_memmove(buffer, p, *buffer_off);
4402 return;
4403 }
4404 *buffer_off = 0;
4405 }
4406# endif /* FEAT_MBYTE */
4407 else
4408 {
4409 buffer[len] = NUL;
4410 msg_puts(buffer);
4411 }
4412
4413 windgoto(msg_row, msg_col);
4414 cursor_on();
4415 out_flush();
4416 }
4417}
4418
4419/*
4420 * Version of system to use for windows NT > 5.0 (Win2K), use pipe
4421 * for communication and doesn't open any new window.
4422 */
4423 static int
4424mch_system_piped(char *cmd, int options)
4425{
4426 STARTUPINFO si;
4427 PROCESS_INFORMATION pi;
4428 DWORD ret = 0;
4429
4430 HANDLE g_hChildStd_IN_Rd = NULL;
4431 HANDLE g_hChildStd_IN_Wr = NULL;
4432 HANDLE g_hChildStd_OUT_Rd = NULL;
4433 HANDLE g_hChildStd_OUT_Wr = NULL;
4434
4435 char_u buffer[BUFLEN + 1]; /* reading buffer + size */
4436 DWORD len;
4437
4438 /* buffer used to receive keys */
4439 char_u ta_buf[BUFLEN + 1]; /* TypeAHead */
4440 int ta_len = 0; /* valid bytes in ta_buf[] */
4441
4442 DWORD i;
4443 int c;
4444 int noread_cnt = 0;
4445 garray_T ga;
4446 int delay = 1;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004447 DWORD buffer_off = 0; /* valid bytes in buffer[] */
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004448 char *p = NULL;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004449
4450 SECURITY_ATTRIBUTES saAttr;
4451
4452 /* Set the bInheritHandle flag so pipe handles are inherited. */
4453 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
4454 saAttr.bInheritHandle = TRUE;
4455 saAttr.lpSecurityDescriptor = NULL;
4456
4457 if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)
4458 /* Ensure the read handle to the pipe for STDOUT is not inherited. */
4459 || ! pSetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)
4460 /* Create a pipe for the child process's STDIN. */
4461 || ! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)
4462 /* Ensure the write handle to the pipe for STDIN is not inherited. */
4463 || ! pSetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
4464 {
4465 CloseHandle(g_hChildStd_IN_Rd);
4466 CloseHandle(g_hChildStd_IN_Wr);
4467 CloseHandle(g_hChildStd_OUT_Rd);
4468 CloseHandle(g_hChildStd_OUT_Wr);
4469 MSG_PUTS(_("\nCannot create pipes\n"));
4470 }
4471
4472 si.cb = sizeof(si);
4473 si.lpReserved = NULL;
4474 si.lpDesktop = NULL;
4475 si.lpTitle = NULL;
4476 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
4477
4478 /* set-up our file redirection */
4479 si.hStdError = g_hChildStd_OUT_Wr;
4480 si.hStdOutput = g_hChildStd_OUT_Wr;
4481 si.hStdInput = g_hChildStd_IN_Rd;
4482 si.wShowWindow = SW_HIDE;
4483 si.cbReserved2 = 0;
4484 si.lpReserved2 = NULL;
4485
4486 if (options & SHELL_READ)
4487 ga_init2(&ga, 1, BUFLEN);
4488
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004489 if (cmd != NULL)
4490 {
4491 p = (char *)vim_strsave((char_u *)cmd);
4492 if (p != NULL)
4493 unescape_shellxquote((char_u *)p, p_sxe);
4494 else
4495 p = cmd;
4496 }
4497
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004498 /* Now, run the command.
4499 * About "Inherit handles" being TRUE: this command can be litigious,
4500 * handle inheritance was deactivated for pending temp file, but, if we
4501 * deactivate it, the pipes don't work for some reason. */
4502 vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE, &si, &pi);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004503
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004504 if (p != cmd)
4505 vim_free(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004506
4507 /* Close our unused side of the pipes */
4508 CloseHandle(g_hChildStd_IN_Rd);
4509 CloseHandle(g_hChildStd_OUT_Wr);
4510
4511 if (options & SHELL_WRITE)
4512 {
4513 HANDLE thread =
4514 CreateThread(NULL, /* security attributes */
4515 0, /* default stack size */
4516 sub_process_writer, /* function to be executed */
4517 g_hChildStd_IN_Wr, /* parameter */
4518 0, /* creation flag, start immediately */
4519 NULL); /* we don't care about thread id */
4520 CloseHandle(thread);
4521 g_hChildStd_IN_Wr = NULL;
4522 }
4523
4524 /* Keep updating the window while waiting for the shell to finish. */
4525 for (;;)
4526 {
4527 MSG msg;
4528
Bram Moolenaar175d0702013-12-11 18:36:33 +01004529 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004530 {
4531 TranslateMessage(&msg);
Bram Moolenaar175d0702013-12-11 18:36:33 +01004532 pDispatchMessage(&msg);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004533 }
4534
4535 /* write pipe information in the window */
4536 if ((options & (SHELL_READ|SHELL_WRITE))
4537# ifdef FEAT_GUI
4538 || gui.in_use
4539# endif
4540 )
4541 {
4542 len = 0;
4543 if (!(options & SHELL_EXPAND)
4544 && ((options &
4545 (SHELL_READ|SHELL_WRITE|SHELL_COOKED))
4546 != (SHELL_READ|SHELL_WRITE|SHELL_COOKED)
4547# ifdef FEAT_GUI
4548 || gui.in_use
4549# endif
4550 )
4551 && (ta_len > 0 || noread_cnt > 4))
4552 {
4553 if (ta_len == 0)
4554 {
4555 /* Get extra characters when we don't have any. Reset the
4556 * counter and timer. */
4557 noread_cnt = 0;
4558# if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
4559 gettimeofday(&start_tv, NULL);
4560# endif
4561 len = ui_inchar(ta_buf, BUFLEN, 10L, 0);
4562 }
4563 if (ta_len > 0 || len > 0)
4564 {
4565 /*
4566 * For pipes: Check for CTRL-C: send interrupt signal to
4567 * child. Check for CTRL-D: EOF, close pipe to child.
4568 */
4569 if (len == 1 && cmd != NULL)
4570 {
4571 if (ta_buf[ta_len] == Ctrl_C)
4572 {
4573 /* Learn what exit code is expected, for
4574 * now put 9 as SIGKILL */
4575 TerminateProcess(pi.hProcess, 9);
4576 }
4577 if (ta_buf[ta_len] == Ctrl_D)
4578 {
4579 CloseHandle(g_hChildStd_IN_Wr);
4580 g_hChildStd_IN_Wr = NULL;
4581 }
4582 }
4583
4584 /* replace K_BS by <BS> and K_DEL by <DEL> */
4585 for (i = ta_len; i < ta_len + len; ++i)
4586 {
4587 if (ta_buf[i] == CSI && len - i > 2)
4588 {
4589 c = TERMCAP2KEY(ta_buf[i + 1], ta_buf[i + 2]);
4590 if (c == K_DEL || c == K_KDEL || c == K_BS)
4591 {
4592 mch_memmove(ta_buf + i + 1, ta_buf + i + 3,
4593 (size_t)(len - i - 2));
4594 if (c == K_DEL || c == K_KDEL)
4595 ta_buf[i] = DEL;
4596 else
4597 ta_buf[i] = Ctrl_H;
4598 len -= 2;
4599 }
4600 }
4601 else if (ta_buf[i] == '\r')
4602 ta_buf[i] = '\n';
4603# ifdef FEAT_MBYTE
4604 if (has_mbyte)
4605 i += (*mb_ptr2len_len)(ta_buf + i,
4606 ta_len + len - i) - 1;
4607# endif
4608 }
4609
4610 /*
4611 * For pipes: echo the typed characters. For a pty this
4612 * does not seem to work.
4613 */
4614 for (i = ta_len; i < ta_len + len; ++i)
4615 {
4616 if (ta_buf[i] == '\n' || ta_buf[i] == '\b')
4617 msg_putchar(ta_buf[i]);
4618# ifdef FEAT_MBYTE
4619 else if (has_mbyte)
4620 {
4621 int l = (*mb_ptr2len)(ta_buf + i);
4622
4623 msg_outtrans_len(ta_buf + i, l);
4624 i += l - 1;
4625 }
4626# endif
4627 else
4628 msg_outtrans_len(ta_buf + i, 1);
4629 }
4630 windgoto(msg_row, msg_col);
4631 out_flush();
4632
4633 ta_len += len;
4634
4635 /*
4636 * Write the characters to the child, unless EOF has been
4637 * typed for pipes. Write one character at a time, to
4638 * avoid losing too much typeahead. When writing buffer
4639 * lines, drop the typed characters (only check for
4640 * CTRL-C).
4641 */
4642 if (options & SHELL_WRITE)
4643 ta_len = 0;
4644 else if (g_hChildStd_IN_Wr != NULL)
4645 {
4646 WriteFile(g_hChildStd_IN_Wr, (char*)ta_buf,
4647 1, &len, NULL);
4648 // if we are typing in, we want to keep things reactive
4649 delay = 1;
4650 if (len > 0)
4651 {
4652 ta_len -= len;
4653 mch_memmove(ta_buf, ta_buf + len, ta_len);
4654 }
4655 }
4656 }
4657 }
4658 }
4659
4660 if (ta_len)
4661 ui_inchar_undo(ta_buf, ta_len);
4662
4663 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4664 {
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004665 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004666 break;
4667 }
4668
4669 ++noread_cnt;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004670 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004671
4672 /* We start waiting for a very short time and then increase it, so
4673 * that we respond quickly when the process is quick, and don't
4674 * consume too much overhead when it's slow. */
4675 if (delay < 50)
4676 delay += 10;
4677 }
4678
4679 /* Close the pipe */
4680 CloseHandle(g_hChildStd_OUT_Rd);
4681 if (g_hChildStd_IN_Wr != NULL)
4682 CloseHandle(g_hChildStd_IN_Wr);
4683
4684 WaitForSingleObject(pi.hProcess, INFINITE);
4685
4686 /* Get the command exit code */
4687 GetExitCodeProcess(pi.hProcess, &ret);
4688
4689 if (options & SHELL_READ)
4690 {
4691 if (ga.ga_len > 0)
4692 {
4693 append_ga_line(&ga);
4694 /* remember that the NL was missing */
4695 curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
4696 }
4697 else
4698 curbuf->b_no_eol_lnum = 0;
4699 ga_clear(&ga);
4700 }
4701
4702 /* Close the handles to the subprocess, so that it goes away */
4703 CloseHandle(pi.hThread);
4704 CloseHandle(pi.hProcess);
4705
4706 return ret;
4707}
4708
4709 static int
4710mch_system(char *cmd, int options)
4711{
4712 /* if we can pipe and the shelltemp option is off */
4713 if (allowPiping && !p_stmp)
4714 return mch_system_piped(cmd, options);
4715 else
4716 return mch_system_classic(cmd, options);
4717}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004718#else
4719
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004720# ifdef FEAT_MBYTE
4721 static int
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01004722mch_system(char *cmd, int options)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004723{
4724 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4725 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004726 WCHAR *wcmd = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004727 if (wcmd != NULL)
4728 {
4729 int ret = _wsystem(wcmd);
4730 vim_free(wcmd);
4731 return ret;
4732 }
4733 }
4734 return system(cmd);
4735}
4736# else
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01004737# define mch_system(c, o) system(c)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004738# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004739
4740#endif
4741
4742/*
4743 * Either execute a command by calling the shell or start a new shell
4744 */
4745 int
4746mch_call_shell(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004747 char_u *cmd,
4748 int options) /* SHELL_*, see vim.h */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004749{
4750 int x = 0;
4751 int tmode = cur_tmode;
4752#ifdef FEAT_TITLE
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004753 char szShellTitle[512];
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004754# ifdef FEAT_MBYTE
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004755 int did_set_title = FALSE;
4756
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004757 /* Change the title to reflect that we are in a subshell. */
4758 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4759 {
4760 WCHAR szShellTitle[512];
4761
4762 if (GetConsoleTitleW(szShellTitle,
4763 sizeof(szShellTitle)/sizeof(WCHAR) - 4) > 0)
4764 {
4765 if (cmd == NULL)
4766 wcscat(szShellTitle, L" :sh");
4767 else
4768 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004769 WCHAR *wn = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004770
4771 if (wn != NULL)
4772 {
4773 wcscat(szShellTitle, L" - !");
4774 if ((wcslen(szShellTitle) + wcslen(wn) <
4775 sizeof(szShellTitle)/sizeof(WCHAR)))
4776 wcscat(szShellTitle, wn);
4777 SetConsoleTitleW(szShellTitle);
4778 vim_free(wn);
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004779 did_set_title = TRUE;
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004780 }
4781 }
4782 }
4783 }
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004784 if (!did_set_title)
4785# endif
4786 /* Change the title to reflect that we are in a subshell. */
4787 if (GetConsoleTitle(szShellTitle, sizeof(szShellTitle) - 4) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004788 {
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004789 if (cmd == NULL)
4790 strcat(szShellTitle, " :sh");
4791 else
4792 {
4793 strcat(szShellTitle, " - !");
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004794 if ((strlen(szShellTitle) + strlen((char *)cmd)
4795 < sizeof(szShellTitle)))
4796 strcat(szShellTitle, (char *)cmd);
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004797 }
4798 SetConsoleTitle(szShellTitle);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004799 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004800#endif
4801
4802 out_flush();
4803
4804#ifdef MCH_WRITE_DUMP
4805 if (fdDump)
4806 {
4807 fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
4808 fflush(fdDump);
4809 }
4810#endif
4811
4812 /*
4813 * Catch all deadly signals while running the external command, because a
4814 * CTRL-C, Ctrl-Break or illegal instruction might otherwise kill us.
4815 */
4816 signal(SIGINT, SIG_IGN);
4817#if defined(__GNUC__) && !defined(__MINGW32__)
4818 signal(SIGKILL, SIG_IGN);
4819#else
4820 signal(SIGBREAK, SIG_IGN);
4821#endif
4822 signal(SIGILL, SIG_IGN);
4823 signal(SIGFPE, SIG_IGN);
4824 signal(SIGSEGV, SIG_IGN);
4825 signal(SIGTERM, SIG_IGN);
4826 signal(SIGABRT, SIG_IGN);
4827
4828 if (options & SHELL_COOKED)
4829 settmode(TMODE_COOK); /* set to normal mode */
4830
4831 if (cmd == NULL)
4832 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004833 x = mch_system((char *)p_sh, options);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004834 }
4835 else
4836 {
4837 /* we use "command" or "cmd" to start the shell; slow but easy */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004838 char_u *newcmd = NULL;
4839 char_u *cmdbase = cmd;
4840 long_u cmdlen;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004841
4842 /* Skip a leading ", ( and "(. */
4843 if (*cmdbase == '"' )
4844 ++cmdbase;
4845 if (*cmdbase == '(')
4846 ++cmdbase;
4847
4848 if ((STRNICMP(cmdbase, "start", 5) == 0) && vim_iswhite(cmdbase[5]))
4849 {
4850 STARTUPINFO si;
4851 PROCESS_INFORMATION pi;
4852 DWORD flags = CREATE_NEW_CONSOLE;
4853 char_u *p;
4854
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01004855 ZeroMemory(&si, sizeof(si));
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004856 si.cb = sizeof(si);
4857 si.lpReserved = NULL;
4858 si.lpDesktop = NULL;
4859 si.lpTitle = NULL;
4860 si.dwFlags = 0;
4861 si.cbReserved2 = 0;
4862 si.lpReserved2 = NULL;
4863
4864 cmdbase = skipwhite(cmdbase + 5);
4865 if ((STRNICMP(cmdbase, "/min", 4) == 0)
4866 && vim_iswhite(cmdbase[4]))
4867 {
4868 cmdbase = skipwhite(cmdbase + 4);
4869 si.dwFlags = STARTF_USESHOWWINDOW;
4870 si.wShowWindow = SW_SHOWMINNOACTIVE;
4871 }
4872 else if ((STRNICMP(cmdbase, "/b", 2) == 0)
4873 && vim_iswhite(cmdbase[2]))
4874 {
4875 cmdbase = skipwhite(cmdbase + 2);
4876 flags = CREATE_NO_WINDOW;
4877 si.dwFlags = STARTF_USESTDHANDLES;
4878 si.hStdInput = CreateFile("\\\\.\\NUL", // File name
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004879 GENERIC_READ, // Access flags
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004880 0, // Share flags
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004881 NULL, // Security att.
4882 OPEN_EXISTING, // Open flags
4883 FILE_ATTRIBUTE_NORMAL, // File att.
4884 NULL); // Temp file
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004885 si.hStdOutput = si.hStdInput;
4886 si.hStdError = si.hStdInput;
4887 }
4888
4889 /* Remove a trailing ", ) and )" if they have a match
4890 * at the start of the command. */
4891 if (cmdbase > cmd)
4892 {
4893 p = cmdbase + STRLEN(cmdbase);
4894 if (p > cmdbase && p[-1] == '"' && *cmd == '"')
4895 *--p = NUL;
4896 if (p > cmdbase && p[-1] == ')'
4897 && (*cmd =='(' || cmd[1] == '('))
4898 *--p = NUL;
4899 }
4900
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004901 newcmd = cmdbase;
4902 unescape_shellxquote(cmdbase, p_sxe);
4903
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004904 /*
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004905 * If creating new console, arguments are passed to the
4906 * 'cmd.exe' as-is. If it's not, arguments are not treated
4907 * correctly for current 'cmd.exe'. So unescape characters in
4908 * shellxescape except '|' for avoiding to be treated as
4909 * argument to them. Pass the arguments to sub-shell.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004910 */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004911 if (flags != CREATE_NEW_CONSOLE)
4912 {
4913 char_u *subcmd;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004914 char_u *cmd_shell = mch_getenv("COMSPEC");
4915
4916 if (cmd_shell == NULL || *cmd_shell == NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004917 cmd_shell = (char_u *)default_shell();
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004918
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004919 subcmd = vim_strsave_escaped_ext(cmdbase,
4920 (char_u *)"|", '^', FALSE);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004921 if (subcmd != NULL)
4922 {
4923 /* make "cmd.exe /c arguments" */
4924 cmdlen = STRLEN(cmd_shell) + STRLEN(subcmd) + 5;
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004925 newcmd = lalloc(cmdlen, TRUE);
4926 if (newcmd != NULL)
4927 vim_snprintf((char *)newcmd, cmdlen, "%s /c %s",
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004928 cmd_shell, subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004929 else
4930 newcmd = cmdbase;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004931 vim_free(subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004932 }
4933 }
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004934
4935 /*
4936 * Now, start the command as a process, so that it doesn't
4937 * inherit our handles which causes unpleasant dangling swap
4938 * files if we exit before the spawned process
4939 */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004940 if (vim_create_process((char *)newcmd, FALSE, flags, &si, &pi))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004941 x = 0;
4942 else
4943 {
4944 x = -1;
4945#ifdef FEAT_GUI_W32
4946 EMSG(_("E371: Command not found"));
4947#endif
4948 }
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004949
4950 if (newcmd != cmdbase)
4951 vim_free(newcmd);
4952
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01004953 if (si.dwFlags == STARTF_USESTDHANDLES && si.hStdInput != NULL)
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004954 {
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01004955 /* Close the handle to \\.\NUL created above. */
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004956 CloseHandle(si.hStdInput);
4957 }
4958 /* Close the handles to the subprocess, so that it goes away */
4959 CloseHandle(pi.hThread);
4960 CloseHandle(pi.hProcess);
4961 }
4962 else
4963 {
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004964 cmdlen = (
Bram Moolenaar071d4272004-06-13 20:20:40 +00004965#ifdef FEAT_GUI_W32
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004966 (allowPiping && !p_stmp ? 0 : STRLEN(vimrun_path)) +
Bram Moolenaar071d4272004-06-13 20:20:40 +00004967#endif
Bram Moolenaar0fde2902008-03-16 13:54:13 +00004968 STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10);
4969
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004970 newcmd = lalloc(cmdlen, TRUE);
4971 if (newcmd != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004972 {
4973#if defined(FEAT_GUI_W32)
4974 if (need_vimrun_warning)
4975 {
4976 MessageBox(NULL,
4977 _("VIMRUN.EXE not found in your $PATH.\n"
4978 "External commands will not pause after completion.\n"
4979 "See :help win32-vimrun for more information."),
4980 _("Vim Warning"),
4981 MB_ICONWARNING);
4982 need_vimrun_warning = FALSE;
4983 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004984 if (!s_dont_use_vimrun && (!allowPiping || p_stmp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004985 /* Use vimrun to execute the command. It opens a console
4986 * window, which can be closed without killing Vim. */
Bram Moolenaarcc448b32010-07-14 16:52:17 +02004987 vim_snprintf((char *)newcmd, cmdlen, "%s%s%s %s %s",
Bram Moolenaar071d4272004-06-13 20:20:40 +00004988 vimrun_path,
4989 (msg_silent != 0 || (options & SHELL_DOOUT))
4990 ? "-s " : "",
4991 p_sh, p_shcf, cmd);
4992 else
4993#endif
Bram Moolenaarcc448b32010-07-14 16:52:17 +02004994 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s",
Bram Moolenaar0fde2902008-03-16 13:54:13 +00004995 p_sh, p_shcf, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004996 x = mch_system((char *)newcmd, options);
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004997 vim_free(newcmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004998 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004999 }
5000 }
5001
5002 if (tmode == TMODE_RAW)
5003 settmode(TMODE_RAW); /* set to raw mode */
5004
5005 /* Print the return value, unless "vimrun" was used. */
5006 if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent
5007#if defined(FEAT_GUI_W32)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005008 && ((options & SHELL_DOOUT) || s_dont_use_vimrun
5009 || (allowPiping && !p_stmp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005010#endif
5011 )
5012 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005013 smsg((char_u *)_("shell returned %d"), x);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005014 msg_putchar('\n');
5015 }
5016#ifdef FEAT_TITLE
5017 resettitle();
5018#endif
5019
5020 signal(SIGINT, SIG_DFL);
5021#if defined(__GNUC__) && !defined(__MINGW32__)
5022 signal(SIGKILL, SIG_DFL);
5023#else
5024 signal(SIGBREAK, SIG_DFL);
5025#endif
5026 signal(SIGILL, SIG_DFL);
5027 signal(SIGFPE, SIG_DFL);
5028 signal(SIGSEGV, SIG_DFL);
5029 signal(SIGTERM, SIG_DFL);
5030 signal(SIGABRT, SIG_DFL);
5031
5032 return x;
5033}
5034
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005035#if defined(FEAT_JOB) || defined(PROTO)
5036 void
5037mch_start_job(char *cmd, job_T *job)
5038{
5039 STARTUPINFO si;
5040 PROCESS_INFORMATION pi;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005041 HANDLE jo;
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005042# ifdef FEAT_CHANNEL
5043 channel_T *channel;
Bram Moolenaard8070362016-02-15 21:56:54 +01005044 HANDLE ifd[2];
5045 HANDLE ofd[2];
5046 HANDLE efd[2];
5047 SECURITY_ATTRIBUTES saAttr;
5048
5049 ifd[0] = INVALID_HANDLE_VALUE;
5050 ifd[1] = INVALID_HANDLE_VALUE;
5051 ofd[0] = INVALID_HANDLE_VALUE;
5052 ofd[1] = INVALID_HANDLE_VALUE;
5053 efd[0] = INVALID_HANDLE_VALUE;
5054 efd[1] = INVALID_HANDLE_VALUE;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005055
5056 channel = add_channel();
5057 if (channel == NULL)
5058 return;
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005059# endif
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005060
Bram Moolenaar76467df2016-02-12 19:30:26 +01005061 jo = CreateJobObject(NULL, NULL);
5062 if (jo == NULL)
5063 {
5064 job->jv_status = JOB_FAILED;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005065 goto failed;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005066 }
5067
5068 ZeroMemory(&pi, sizeof(pi));
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005069 ZeroMemory(&si, sizeof(si));
5070 si.cb = sizeof(si);
Bram Moolenaard8070362016-02-15 21:56:54 +01005071 si.dwFlags |= STARTF_USESHOWWINDOW;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005072 si.wShowWindow = SW_HIDE;
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005073
Bram Moolenaard8070362016-02-15 21:56:54 +01005074 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
5075 saAttr.bInheritHandle = TRUE;
5076 saAttr.lpSecurityDescriptor = NULL;
5077 if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
5078 || !pSetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)
5079 || !CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
5080 || !pSetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)
5081 || !CreatePipe(&efd[0], &efd[1], &saAttr, 0)
5082 || !pSetHandleInformation(efd[0], HANDLE_FLAG_INHERIT, 0))
5083 goto failed;
5084 si.dwFlags |= STARTF_USESTDHANDLES;
5085 si.hStdInput = ifd[0];
5086 si.hStdOutput = ofd[1];
5087 si.hStdError = efd[1];
5088
5089 if (!vim_create_process(cmd, TRUE,
Bram Moolenaar76467df2016-02-12 19:30:26 +01005090 CREATE_SUSPENDED |
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005091 CREATE_DEFAULT_ERROR_MODE |
5092 CREATE_NEW_PROCESS_GROUP |
Bram Moolenaar76467df2016-02-12 19:30:26 +01005093 CREATE_NEW_CONSOLE,
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005094 &si, &pi))
Bram Moolenaar76467df2016-02-12 19:30:26 +01005095 {
5096 CloseHandle(jo);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005097 job->jv_status = JOB_FAILED;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005098 goto failed;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005099 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005100
5101 if (!AssignProcessToJobObject(jo, pi.hProcess))
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005102 {
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005103 /* if failing, switch the way to terminate
5104 * process with TerminateProcess. */
5105 CloseHandle(jo);
5106 jo = NULL;
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005107 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005108 ResumeThread(pi.hThread);
5109 CloseHandle(job->jv_proc_info.hThread);
5110 job->jv_proc_info = pi;
5111 job->jv_job_object = jo;
5112 job->jv_status = JOB_STARTED;
5113
Bram Moolenaard8070362016-02-15 21:56:54 +01005114 CloseHandle(ifd[0]);
5115 CloseHandle(ofd[1]);
5116 CloseHandle(efd[1]);
5117
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005118# ifdef FEAT_CHANNEL
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005119 job->jv_channel = channel;
Bram Moolenaard8070362016-02-15 21:56:54 +01005120 channel_set_pipes(channel, (sock_T)ifd[1], (sock_T)ofd[0], (sock_T)efd[0]);
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005121 channel_set_job(channel, job);
5122
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005123# ifdef FEAT_GUI
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005124 channel_gui_register(channel);
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005125# endif
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005126# endif
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005127 return;
5128
5129failed:
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005130# ifdef FEAT_CHANNEL
Bram Moolenaard8070362016-02-15 21:56:54 +01005131 CloseHandle(ifd[0]);
5132 CloseHandle(ofd[0]);
5133 CloseHandle(efd[0]);
5134 CloseHandle(ifd[1]);
5135 CloseHandle(ofd[1]);
5136 CloseHandle(efd[1]);
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005137 channel_free(channel);
Bram Moolenaar71b0f7b2016-02-15 12:44:20 +01005138# else
5139 ; /* make compiler happy */
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005140# endif
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005141}
5142
5143 char *
5144mch_job_status(job_T *job)
5145{
5146 DWORD dwExitCode = 0;
5147
Bram Moolenaar76467df2016-02-12 19:30:26 +01005148 if (!GetExitCodeProcess(job->jv_proc_info.hProcess, &dwExitCode)
5149 || dwExitCode != STILL_ACTIVE)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005150 {
Bram Moolenaar76467df2016-02-12 19:30:26 +01005151 job->jv_status = JOB_ENDED;
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005152 return "dead";
5153 }
5154 return "run";
5155}
5156
5157 int
5158mch_stop_job(job_T *job, char_u *how)
5159{
Bram Moolenaar76467df2016-02-12 19:30:26 +01005160 int ret = 0;
5161 int ctrl_c = STRCMP(how, "int") == 0;
5162
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005163 if (STRCMP(how, "kill") == 0)
Bram Moolenaar76467df2016-02-12 19:30:26 +01005164 {
5165 if (job->jv_job_object != NULL)
5166 return TerminateJobObject(job->jv_job_object, 0) ? OK : FAIL;
5167 else
5168 return TerminateProcess(job->jv_proc_info.hProcess, 0) ? OK : FAIL;
5169 }
5170
5171 if (!AttachConsole(job->jv_proc_info.dwProcessId))
5172 return FAIL;
5173 ret = GenerateConsoleCtrlEvent(
5174 ctrl_c ? CTRL_C_EVENT : CTRL_BREAK_EVENT,
5175 job->jv_proc_info.dwProcessId)
5176 ? OK : FAIL;
5177 FreeConsole();
5178 return ret;
5179}
5180
5181/*
5182 * Clear the data related to "job".
5183 */
5184 void
5185mch_clear_job(job_T *job)
5186{
5187 if (job->jv_status != JOB_FAILED)
5188 {
5189 if (job->jv_job_object != NULL)
5190 CloseHandle(job->jv_job_object);
5191 CloseHandle(job->jv_proc_info.hProcess);
5192 }
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005193}
5194#endif
5195
Bram Moolenaar071d4272004-06-13 20:20:40 +00005196
5197#ifndef FEAT_GUI_W32
5198
5199/*
5200 * Start termcap mode
5201 */
5202 static void
5203termcap_mode_start(void)
5204{
5205 DWORD cmodein;
5206
5207 if (g_fTermcapMode)
5208 return;
5209
5210 SaveConsoleBuffer(&g_cbNonTermcap);
5211
5212 if (g_cbTermcap.IsValid)
5213 {
5214 /*
5215 * We've been in termcap mode before. Restore certain screen
5216 * characteristics, including the buffer size and the window
5217 * size. Since we will be redrawing the screen, we don't need
5218 * to restore the actual contents of the buffer.
5219 */
5220 RestoreConsoleBuffer(&g_cbTermcap, FALSE);
5221 SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow);
5222 Rows = g_cbTermcap.Info.dwSize.Y;
5223 Columns = g_cbTermcap.Info.dwSize.X;
5224 }
5225 else
5226 {
5227 /*
5228 * This is our first time entering termcap mode. Clear the console
5229 * screen buffer, and resize the buffer to match the current window
5230 * size. We will use this as the size of our editing environment.
5231 */
5232 ClearConsoleBuffer(g_attrCurrent);
5233 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
5234 }
5235
5236#ifdef FEAT_TITLE
5237 resettitle();
5238#endif
5239
5240 GetConsoleMode(g_hConIn, &cmodein);
5241#ifdef FEAT_MOUSE
5242 if (g_fMouseActive)
5243 cmodein |= ENABLE_MOUSE_INPUT;
5244 else
5245 cmodein &= ~ENABLE_MOUSE_INPUT;
5246#endif
5247 cmodein |= ENABLE_WINDOW_INPUT;
5248 SetConsoleMode(g_hConIn, cmodein);
5249
5250 redraw_later_clear();
5251 g_fTermcapMode = TRUE;
5252}
5253
5254
5255/*
5256 * End termcap mode
5257 */
5258 static void
5259termcap_mode_end(void)
5260{
5261 DWORD cmodein;
5262 ConsoleBuffer *cb;
5263 COORD coord;
5264 DWORD dwDummy;
5265
5266 if (!g_fTermcapMode)
5267 return;
5268
5269 SaveConsoleBuffer(&g_cbTermcap);
5270
5271 GetConsoleMode(g_hConIn, &cmodein);
5272 cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
5273 SetConsoleMode(g_hConIn, cmodein);
5274
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01005275#ifdef FEAT_RESTORE_ORIG_SCREEN
5276 cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
5277#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005278 cb = &g_cbNonTermcap;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01005279#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005280 RestoreConsoleBuffer(cb, p_rs);
5281 SetConsoleCursorInfo(g_hConOut, &g_cci);
5282
5283 if (p_rs || exiting)
5284 {
5285 /*
5286 * Clear anything that happens to be on the current line.
5287 */
5288 coord.X = 0;
5289 coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1));
5290 FillConsoleOutputCharacter(g_hConOut, ' ',
5291 cb->Info.dwSize.X, coord, &dwDummy);
5292 /*
5293 * The following is just for aesthetics. If we are exiting without
5294 * restoring the screen, then we want to have a prompt string
5295 * appear at the bottom line. However, the command interpreter
5296 * seems to always advance the cursor one line before displaying
5297 * the prompt string, which causes the screen to scroll. To
5298 * counter this, move the cursor up one line before exiting.
5299 */
5300 if (exiting && !p_rs)
5301 coord.Y--;
5302 /*
5303 * Position the cursor at the leftmost column of the desired row.
5304 */
5305 SetConsoleCursorPosition(g_hConOut, coord);
5306 }
5307
5308 g_fTermcapMode = FALSE;
5309}
5310#endif /* FEAT_GUI_W32 */
5311
5312
5313#ifdef FEAT_GUI_W32
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00005314/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00005315 void
5316mch_write(
5317 char_u *s,
5318 int len)
5319{
5320 /* never used */
5321}
5322
5323#else
5324
5325/*
5326 * clear `n' chars, starting from `coord'
5327 */
5328 static void
5329clear_chars(
5330 COORD coord,
5331 DWORD n)
5332{
5333 DWORD dwDummy;
5334
5335 FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
5336 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy);
5337}
5338
5339
5340/*
5341 * Clear the screen
5342 */
5343 static void
5344clear_screen(void)
5345{
5346 g_coord.X = g_coord.Y = 0;
5347 clear_chars(g_coord, Rows * Columns);
5348}
5349
5350
5351/*
5352 * Clear to end of display
5353 */
5354 static void
5355clear_to_end_of_display(void)
5356{
5357 clear_chars(g_coord, (Rows - g_coord.Y - 1)
5358 * Columns + (Columns - g_coord.X));
5359}
5360
5361
5362/*
5363 * Clear to end of line
5364 */
5365 static void
5366clear_to_end_of_line(void)
5367{
5368 clear_chars(g_coord, Columns - g_coord.X);
5369}
5370
5371
5372/*
5373 * Scroll the scroll region up by `cLines' lines
5374 */
5375 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005376scroll(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005377{
5378 COORD oldcoord = g_coord;
5379
5380 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
5381 delete_lines(cLines);
5382
5383 g_coord = oldcoord;
5384}
5385
5386
5387/*
5388 * Set the scroll region
5389 */
5390 static void
5391set_scroll_region(
5392 unsigned left,
5393 unsigned top,
5394 unsigned right,
5395 unsigned bottom)
5396{
5397 if (left >= right
5398 || top >= bottom
5399 || right > (unsigned) Columns - 1
5400 || bottom > (unsigned) Rows - 1)
5401 return;
5402
5403 g_srScrollRegion.Left = left;
5404 g_srScrollRegion.Top = top;
5405 g_srScrollRegion.Right = right;
5406 g_srScrollRegion.Bottom = bottom;
5407}
5408
5409
5410/*
5411 * Insert `cLines' lines at the current cursor position
5412 */
5413 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005414insert_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005415{
5416 SMALL_RECT source;
5417 COORD dest;
5418 CHAR_INFO fill;
5419
5420 dest.X = 0;
5421 dest.Y = g_coord.Y + cLines;
5422
5423 source.Left = 0;
5424 source.Top = g_coord.Y;
5425 source.Right = g_srScrollRegion.Right;
5426 source.Bottom = g_srScrollRegion.Bottom - cLines;
5427
5428 fill.Char.AsciiChar = ' ';
5429 fill.Attributes = g_attrCurrent;
5430
5431 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
5432
5433 /* Here we have to deal with a win32 console flake: If the scroll
5434 * region looks like abc and we scroll c to a and fill with d we get
5435 * cbd... if we scroll block c one line at a time to a, we get cdd...
5436 * vim expects cdd consistently... So we have to deal with that
5437 * here... (this also occurs scrolling the same way in the other
5438 * direction). */
5439
5440 if (source.Bottom < dest.Y)
5441 {
5442 COORD coord;
5443
5444 coord.X = 0;
5445 coord.Y = source.Bottom;
5446 clear_chars(coord, Columns * (dest.Y - source.Bottom));
5447 }
5448}
5449
5450
5451/*
5452 * Delete `cLines' lines at the current cursor position
5453 */
5454 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005455delete_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005456{
5457 SMALL_RECT source;
5458 COORD dest;
5459 CHAR_INFO fill;
5460 int nb;
5461
5462 dest.X = 0;
5463 dest.Y = g_coord.Y;
5464
5465 source.Left = 0;
5466 source.Top = g_coord.Y + cLines;
5467 source.Right = g_srScrollRegion.Right;
5468 source.Bottom = g_srScrollRegion.Bottom;
5469
5470 fill.Char.AsciiChar = ' ';
5471 fill.Attributes = g_attrCurrent;
5472
5473 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
5474
5475 /* Here we have to deal with a win32 console flake: If the scroll
5476 * region looks like abc and we scroll c to a and fill with d we get
5477 * cbd... if we scroll block c one line at a time to a, we get cdd...
5478 * vim expects cdd consistently... So we have to deal with that
5479 * here... (this also occurs scrolling the same way in the other
5480 * direction). */
5481
5482 nb = dest.Y + (source.Bottom - source.Top) + 1;
5483
5484 if (nb < source.Top)
5485 {
5486 COORD coord;
5487
5488 coord.X = 0;
5489 coord.Y = nb;
5490 clear_chars(coord, Columns * (source.Top - nb));
5491 }
5492}
5493
5494
5495/*
5496 * Set the cursor position
5497 */
5498 static void
5499gotoxy(
5500 unsigned x,
5501 unsigned y)
5502{
5503 if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows)
5504 return;
5505
5506 /* external cursor coords are 1-based; internal are 0-based */
5507 g_coord.X = x - 1;
5508 g_coord.Y = y - 1;
5509 SetConsoleCursorPosition(g_hConOut, g_coord);
5510}
5511
5512
5513/*
5514 * Set the current text attribute = (foreground | background)
5515 * See ../doc/os_win32.txt for the numbers.
5516 */
5517 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005518textattr(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005519{
Bram Moolenaar6383b922015-03-24 17:12:19 +01005520 g_attrCurrent = wAttr & 0xff;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005521
5522 SetConsoleTextAttribute(g_hConOut, wAttr);
5523}
5524
5525
5526 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005527textcolor(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005528{
Bram Moolenaar6383b922015-03-24 17:12:19 +01005529 g_attrCurrent = (g_attrCurrent & 0xf0) + (wAttr & 0x0f);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005530
5531 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
5532}
5533
5534
5535 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005536textbackground(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005537{
Bram Moolenaar6383b922015-03-24 17:12:19 +01005538 g_attrCurrent = (g_attrCurrent & 0x0f) + ((wAttr & 0x0f) << 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539
5540 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
5541}
5542
5543
5544/*
5545 * restore the default text attribute (whatever we started with)
5546 */
5547 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005548normvideo(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005549{
5550 textattr(g_attrDefault);
5551}
5552
5553
5554static WORD g_attrPreStandout = 0;
5555
5556/*
5557 * Make the text standout, by brightening it
5558 */
5559 static void
5560standout(void)
5561{
5562 g_attrPreStandout = g_attrCurrent;
5563 textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
5564}
5565
5566
5567/*
5568 * Turn off standout mode
5569 */
5570 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005571standend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005572{
5573 if (g_attrPreStandout)
5574 {
5575 textattr(g_attrPreStandout);
5576 g_attrPreStandout = 0;
5577 }
5578}
5579
5580
5581/*
Bram Moolenaarff1d0d42007-05-10 17:24:16 +00005582 * Set normal fg/bg color, based on T_ME. Called when t_me has been set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005583 */
5584 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005585mch_set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005586{
5587 char_u *p;
5588 int n;
5589
5590 cterm_normal_fg_color = (g_attrDefault & 0xf) + 1;
5591 cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1;
5592 if (T_ME[0] == ESC && T_ME[1] == '|')
5593 {
5594 p = T_ME + 2;
5595 n = getdigits(&p);
5596 if (*p == 'm' && n > 0)
5597 {
5598 cterm_normal_fg_color = (n & 0xf) + 1;
5599 cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
5600 }
5601 }
5602}
5603
5604
5605/*
5606 * visual bell: flash the screen
5607 */
5608 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005609visual_bell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005610{
5611 COORD coordOrigin = {0, 0};
5612 WORD attrFlash = ~g_attrCurrent & 0xff;
5613
5614 DWORD dwDummy;
5615 LPWORD oldattrs = (LPWORD)alloc(Rows * Columns * sizeof(WORD));
5616
5617 if (oldattrs == NULL)
5618 return;
5619 ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
5620 coordOrigin, &dwDummy);
5621 FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns,
5622 coordOrigin, &dwDummy);
5623
5624 Sleep(15); /* wait for 15 msec */
5625 WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
5626 coordOrigin, &dwDummy);
5627 vim_free(oldattrs);
5628}
5629
5630
5631/*
5632 * Make the cursor visible or invisible
5633 */
5634 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005635cursor_visible(BOOL fVisible)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005636{
5637 s_cursor_visible = fVisible;
5638#ifdef MCH_CURSOR_SHAPE
5639 mch_update_cursor();
5640#endif
5641}
5642
5643
5644/*
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005645 * write `cbToWrite' bytes in `pchBuf' to the screen
5646 * Returns the number of bytes actually written (at least one).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005647 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005648 static DWORD
Bram Moolenaar071d4272004-06-13 20:20:40 +00005649write_chars(
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005650 char_u *pchBuf,
5651 DWORD cbToWrite)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005652{
5653 COORD coord = g_coord;
5654 DWORD written;
5655
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005656#ifdef FEAT_MBYTE
5657 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
5658 {
5659 static WCHAR *unicodebuf = NULL;
5660 static int unibuflen = 0;
5661 int length;
5662 DWORD n, cchwritten, cells;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005663
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005664 length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite, 0, 0);
5665 if (unicodebuf == NULL || length > unibuflen)
5666 {
5667 vim_free(unicodebuf);
5668 unicodebuf = (WCHAR *)lalloc(length * sizeof(WCHAR), FALSE);
5669 unibuflen = length;
5670 }
5671 MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite,
5672 unicodebuf, unibuflen);
5673
5674 cells = mb_string2cells(pchBuf, cbToWrite);
5675 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cells,
5676 coord, &written);
5677 /* When writing fails or didn't write a single character, pretend one
5678 * character was written, otherwise we get stuck. */
5679 if (WriteConsoleOutputCharacterW(g_hConOut, unicodebuf, length,
5680 coord, &cchwritten) == 0
5681 || cchwritten == 0)
5682 cchwritten = 1;
5683
5684 if (cchwritten == length)
5685 {
5686 written = cbToWrite;
5687 g_coord.X += (SHORT)cells;
5688 }
5689 else
5690 {
5691 char_u *p = pchBuf;
5692 for (n = 0; n < cchwritten; n++)
5693 mb_cptr_adv(p);
5694 written = p - pchBuf;
5695 g_coord.X += (SHORT)mb_string2cells(pchBuf, written);
5696 }
5697 }
5698 else
5699#endif
5700 {
5701 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cbToWrite,
5702 coord, &written);
5703 /* When writing fails or didn't write a single character, pretend one
5704 * character was written, otherwise we get stuck. */
5705 if (WriteConsoleOutputCharacter(g_hConOut, (LPCSTR)pchBuf, cbToWrite,
5706 coord, &written) == 0
5707 || written == 0)
5708 written = 1;
5709
5710 g_coord.X += (SHORT) written;
5711 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005712
5713 while (g_coord.X > g_srScrollRegion.Right)
5714 {
5715 g_coord.X -= (SHORT) Columns;
5716 if (g_coord.Y < g_srScrollRegion.Bottom)
5717 g_coord.Y++;
5718 }
5719
5720 gotoxy(g_coord.X + 1, g_coord.Y + 1);
5721
5722 return written;
5723}
5724
5725
5726/*
5727 * mch_write(): write the output buffer to the screen, translating ESC
5728 * sequences into calls to console output routines.
5729 */
5730 void
5731mch_write(
5732 char_u *s,
5733 int len)
5734{
5735 s[len] = NUL;
5736
5737 if (!term_console)
5738 {
5739 write(1, s, (unsigned)len);
5740 return;
5741 }
5742
5743 /* translate ESC | sequences into faked bios calls */
5744 while (len--)
5745 {
5746 /* optimization: use one single write_chars for runs of text,
5747 * rather than once per character It ain't curses, but it helps. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005748 DWORD prefix = (DWORD)strcspn((char *)s, "\n\r\b\a\033");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005749
5750 if (p_wd)
5751 {
5752 WaitForChar(p_wd);
5753 if (prefix != 0)
5754 prefix = 1;
5755 }
5756
5757 if (prefix != 0)
5758 {
5759 DWORD nWritten;
5760
5761 nWritten = write_chars(s, prefix);
5762#ifdef MCH_WRITE_DUMP
5763 if (fdDump)
5764 {
5765 fputc('>', fdDump);
5766 fwrite(s, sizeof(char_u), nWritten, fdDump);
5767 fputs("<\n", fdDump);
5768 }
5769#endif
5770 len -= (nWritten - 1);
5771 s += nWritten;
5772 }
5773 else if (s[0] == '\n')
5774 {
5775 /* \n, newline: go to the beginning of the next line or scroll */
5776 if (g_coord.Y == g_srScrollRegion.Bottom)
5777 {
5778 scroll(1);
5779 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
5780 }
5781 else
5782 {
5783 gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
5784 }
5785#ifdef MCH_WRITE_DUMP
5786 if (fdDump)
5787 fputs("\\n\n", fdDump);
5788#endif
5789 s++;
5790 }
5791 else if (s[0] == '\r')
5792 {
5793 /* \r, carriage return: go to beginning of line */
5794 gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
5795#ifdef MCH_WRITE_DUMP
5796 if (fdDump)
5797 fputs("\\r\n", fdDump);
5798#endif
5799 s++;
5800 }
5801 else if (s[0] == '\b')
5802 {
5803 /* \b, backspace: move cursor one position left */
5804 if (g_coord.X > g_srScrollRegion.Left)
5805 g_coord.X--;
5806 else if (g_coord.Y > g_srScrollRegion.Top)
5807 {
5808 g_coord.X = g_srScrollRegion.Right;
5809 g_coord.Y--;
5810 }
5811 gotoxy(g_coord.X + 1, g_coord.Y + 1);
5812#ifdef MCH_WRITE_DUMP
5813 if (fdDump)
5814 fputs("\\b\n", fdDump);
5815#endif
5816 s++;
5817 }
5818 else if (s[0] == '\a')
5819 {
5820 /* \a, bell */
5821 MessageBeep(0xFFFFFFFF);
5822#ifdef MCH_WRITE_DUMP
5823 if (fdDump)
5824 fputs("\\a\n", fdDump);
5825#endif
5826 s++;
5827 }
5828 else if (s[0] == ESC && len >= 3-1 && s[1] == '|')
5829 {
5830#ifdef MCH_WRITE_DUMP
Bram Moolenaarc0197e22004-09-13 20:26:32 +00005831 char_u *old_s = s;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005832#endif
Bram Moolenaarc0197e22004-09-13 20:26:32 +00005833 char_u *p;
5834 int arg1 = 0, arg2 = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005835
5836 switch (s[2])
5837 {
5838 /* one or two numeric arguments, separated by ';' */
5839
5840 case '0': case '1': case '2': case '3': case '4':
5841 case '5': case '6': case '7': case '8': case '9':
5842 p = s + 2;
5843 arg1 = getdigits(&p); /* no check for length! */
5844 if (p > s + len)
5845 break;
5846
5847 if (*p == ';')
5848 {
5849 ++p;
5850 arg2 = getdigits(&p); /* no check for length! */
5851 if (p > s + len)
5852 break;
5853
5854 if (*p == 'H')
5855 gotoxy(arg2, arg1);
5856 else if (*p == 'r')
5857 set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
5858 }
5859 else if (*p == 'A')
5860 {
5861 /* move cursor up arg1 lines in same column */
5862 gotoxy(g_coord.X + 1,
5863 max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
5864 }
5865 else if (*p == 'C')
5866 {
5867 /* move cursor right arg1 columns in same line */
5868 gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
5869 g_coord.Y + 1);
5870 }
5871 else if (*p == 'H')
5872 {
5873 gotoxy(1, arg1);
5874 }
5875 else if (*p == 'L')
5876 {
5877 insert_lines(arg1);
5878 }
5879 else if (*p == 'm')
5880 {
5881 if (arg1 == 0)
5882 normvideo();
5883 else
5884 textattr((WORD) arg1);
5885 }
5886 else if (*p == 'f')
5887 {
5888 textcolor((WORD) arg1);
5889 }
5890 else if (*p == 'b')
5891 {
5892 textbackground((WORD) arg1);
5893 }
5894 else if (*p == 'M')
5895 {
5896 delete_lines(arg1);
5897 }
5898
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00005899 len -= (int)(p - s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005900 s = p + 1;
5901 break;
5902
5903
5904 /* Three-character escape sequences */
5905
5906 case 'A':
5907 /* move cursor up one line in same column */
5908 gotoxy(g_coord.X + 1,
5909 max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
5910 goto got3;
5911
5912 case 'B':
5913 visual_bell();
5914 goto got3;
5915
5916 case 'C':
5917 /* move cursor right one column in same line */
5918 gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
5919 g_coord.Y + 1);
5920 goto got3;
5921
5922 case 'E':
5923 termcap_mode_end();
5924 goto got3;
5925
5926 case 'F':
5927 standout();
5928 goto got3;
5929
5930 case 'f':
5931 standend();
5932 goto got3;
5933
5934 case 'H':
5935 gotoxy(1, 1);
5936 goto got3;
5937
5938 case 'j':
5939 clear_to_end_of_display();
5940 goto got3;
5941
5942 case 'J':
5943 clear_screen();
5944 goto got3;
5945
5946 case 'K':
5947 clear_to_end_of_line();
5948 goto got3;
5949
5950 case 'L':
5951 insert_lines(1);
5952 goto got3;
5953
5954 case 'M':
5955 delete_lines(1);
5956 goto got3;
5957
5958 case 'S':
5959 termcap_mode_start();
5960 goto got3;
5961
5962 case 'V':
5963 cursor_visible(TRUE);
5964 goto got3;
5965
5966 case 'v':
5967 cursor_visible(FALSE);
5968 goto got3;
5969
5970 got3:
5971 s += 3;
5972 len -= 2;
5973 }
5974
5975#ifdef MCH_WRITE_DUMP
5976 if (fdDump)
5977 {
5978 fputs("ESC | ", fdDump);
5979 fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
5980 fputc('\n', fdDump);
5981 }
5982#endif
5983 }
5984 else
5985 {
5986 /* Write a single character */
5987 DWORD nWritten;
5988
5989 nWritten = write_chars(s, 1);
5990#ifdef MCH_WRITE_DUMP
5991 if (fdDump)
5992 {
5993 fputc('>', fdDump);
5994 fwrite(s, sizeof(char_u), nWritten, fdDump);
5995 fputs("<\n", fdDump);
5996 }
5997#endif
5998
5999 len -= (nWritten - 1);
6000 s += nWritten;
6001 }
6002 }
6003
6004#ifdef MCH_WRITE_DUMP
6005 if (fdDump)
6006 fflush(fdDump);
6007#endif
6008}
6009
6010#endif /* FEAT_GUI_W32 */
6011
6012
6013/*
6014 * Delay for half a second.
6015 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00006016/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00006017 void
6018mch_delay(
6019 long msec,
6020 int ignoreinput)
6021{
6022#ifdef FEAT_GUI_W32
6023 Sleep((int)msec); /* never wait for input */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006024#else /* Console */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006025 if (ignoreinput)
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006026# ifdef FEAT_MZSCHEME
6027 if (mzthreads_allowed() && p_mzq > 0 && msec > p_mzq)
6028 {
6029 int towait = p_mzq;
6030
6031 /* if msec is large enough, wait by portions in p_mzq */
6032 while (msec > 0)
6033 {
6034 mzvim_check_threads();
6035 if (msec < towait)
6036 towait = msec;
6037 Sleep(towait);
6038 msec -= towait;
6039 }
6040 }
6041 else
6042# endif
6043 Sleep((int)msec);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006044 else
6045 WaitForChar(msec);
6046#endif
6047}
6048
6049
6050/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01006051 * This version of remove is not scared by a readonly (backup) file.
6052 * This can also remove a symbolic link like Unix.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006053 * Return 0 for success, -1 for failure.
6054 */
6055 int
6056mch_remove(char_u *name)
6057{
6058#ifdef FEAT_MBYTE
6059 WCHAR *wn = NULL;
6060 int n;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02006061#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006062
Bram Moolenaar203258c2016-01-17 22:15:16 +01006063 /*
6064 * On Windows, deleting a directory's symbolic link is done by
6065 * RemoveDirectory(): mch_rmdir. It seems unnatural, but it is fact.
6066 */
6067 if (mch_isdir(name) && mch_is_symbolic_link(name))
6068 return mch_rmdir(name);
6069
Bram Moolenaar12b559e2013-06-12 22:41:37 +02006070 win32_setattrs(name, FILE_ATTRIBUTE_NORMAL);
6071
6072#ifdef FEAT_MBYTE
Bram Moolenaar071d4272004-06-13 20:20:40 +00006073 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6074 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006075 wn = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006076 if (wn != NULL)
6077 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006078 n = DeleteFileW(wn) ? 0 : -1;
6079 vim_free(wn);
6080 if (n == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
6081 return n;
6082 /* Retry with non-wide function (for Windows 98). */
6083 }
6084 }
6085#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006086 return DeleteFile((LPCSTR)name) ? 0 : -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006087}
6088
6089
6090/*
6091 * check for an "interrupt signal": CTRL-break or CTRL-C
6092 */
6093 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006094mch_breakcheck(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006095{
6096#ifndef FEAT_GUI_W32 /* never used */
6097 if (g_fCtrlCPressed || g_fCBrkPressed)
6098 {
6099 g_fCtrlCPressed = g_fCBrkPressed = FALSE;
6100 got_int = TRUE;
6101 }
6102#endif
6103}
6104
Bram Moolenaaree273972016-01-02 21:11:51 +01006105/* physical RAM to leave for the OS */
6106#define WINNT_RESERVE_BYTES (256*1024*1024)
6107#define WIN95_RESERVE_BYTES (8*1024*1024)
6108
6109/*
6110 * How much main memory in KiB that can be used by VIM.
6111 */
6112/*ARGSUSED*/
6113 long_u
6114mch_total_mem(int special)
6115{
6116 PlatformId();
6117#if (defined(_MSC_VER) && (WINVER > 0x0400)) || defined(MEMORYSTATUSEX)
6118 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
6119 {
6120 MEMORYSTATUSEX ms;
6121
6122 /* Need to use GlobalMemoryStatusEx() when there is more memory than
6123 * what fits in 32 bits. But it's not always available. */
6124 ms.dwLength = sizeof(MEMORYSTATUSEX);
6125 GlobalMemoryStatusEx(&ms);
6126 if (ms.ullAvailVirtual < ms.ullTotalPhys)
6127 {
6128 /* Process address space fits in physical RAM, use all of it. */
6129 return (long_u)(ms.ullAvailVirtual / 1024);
6130 }
6131 if (ms.ullTotalPhys <= WINNT_RESERVE_BYTES)
6132 {
6133 /* Catch old NT box or perverse hardware setup. */
6134 return (long_u)((ms.ullTotalPhys / 2) / 1024);
6135 }
6136 /* Use physical RAM less reserve for OS + data. */
6137 return (long_u)((ms.ullTotalPhys - WINNT_RESERVE_BYTES) / 1024);
6138 }
6139 else
6140#endif
6141 {
6142 /* Pre-XP or 95 OS handling. */
6143 MEMORYSTATUS ms;
6144 long_u os_reserve_bytes;
6145
6146 ms.dwLength = sizeof(MEMORYSTATUS);
6147 GlobalMemoryStatus(&ms);
6148 if (ms.dwAvailVirtual < ms.dwTotalPhys)
6149 {
6150 /* Process address space fits in physical RAM, use all of it. */
6151 return (long_u)(ms.dwAvailVirtual / 1024);
6152 }
6153 os_reserve_bytes = (g_PlatformId == VER_PLATFORM_WIN32_NT)
6154 ? WINNT_RESERVE_BYTES
6155 : WIN95_RESERVE_BYTES;
6156 if (ms.dwTotalPhys <= os_reserve_bytes)
6157 {
6158 /* Catch old boxes or perverse hardware setup. */
6159 return (long_u)((ms.dwTotalPhys / 2) / 1024);
6160 }
6161 /* Use physical RAM less reserve for OS + data. */
6162 return (long_u)((ms.dwTotalPhys - os_reserve_bytes) / 1024);
6163 }
6164}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006165
Bram Moolenaar071d4272004-06-13 20:20:40 +00006166#ifdef FEAT_MBYTE
6167/*
6168 * Same code as below, but with wide functions and no comments.
6169 * Return 0 for success, non-zero for failure.
6170 */
6171 int
6172mch_wrename(WCHAR *wold, WCHAR *wnew)
6173{
6174 WCHAR *p;
6175 int i;
6176 WCHAR szTempFile[_MAX_PATH + 1];
6177 WCHAR szNewPath[_MAX_PATH + 1];
6178 HANDLE hf;
6179
6180 if (!mch_windows95())
6181 {
6182 p = wold;
6183 for (i = 0; wold[i] != NUL; ++i)
6184 if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':')
6185 && wold[i + 1] != 0)
6186 p = wold + i + 1;
6187 if ((int)(wold + i - p) < 8 || p[6] != '~')
6188 return (MoveFileW(wold, wnew) == 0);
6189 }
6190
6191 if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL)
6192 return -1;
6193 *p = NUL;
6194
6195 if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0)
6196 return -2;
6197
6198 if (!DeleteFileW(szTempFile))
6199 return -3;
6200
6201 if (!MoveFileW(wold, szTempFile))
6202 return -4;
6203
6204 if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW,
6205 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
6206 return -5;
6207 if (!CloseHandle(hf))
6208 return -6;
6209
6210 if (!MoveFileW(szTempFile, wnew))
6211 {
6212 (void)MoveFileW(szTempFile, wold);
6213 return -7;
6214 }
6215
6216 DeleteFileW(szTempFile);
6217
6218 if (!DeleteFileW(wold))
6219 return -8;
6220
6221 return 0;
6222}
6223#endif
6224
6225
6226/*
6227 * mch_rename() works around a bug in rename (aka MoveFile) in
6228 * Windows 95: rename("foo.bar", "foo.bar~") will generate a
6229 * file whose short file name is "FOO.BAR" (its long file name will
6230 * be correct: "foo.bar~"). Because a file can be accessed by
6231 * either its SFN or its LFN, "foo.bar" has effectively been
6232 * renamed to "foo.bar", which is not at all what was wanted. This
6233 * seems to happen only when renaming files with three-character
6234 * extensions by appending a suffix that does not include ".".
6235 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
6236 *
6237 * There is another problem, which isn't really a bug but isn't right either:
6238 * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be
6239 * "abcdef~1.txt" again. This has been reported on Windows NT 4.0 with
6240 * service pack 6. Doesn't seem to happen on Windows 98.
6241 *
6242 * Like rename(), returns 0 upon success, non-zero upon failure.
6243 * Should probably set errno appropriately when errors occur.
6244 */
6245 int
6246mch_rename(
6247 const char *pszOldFile,
6248 const char *pszNewFile)
6249{
6250 char szTempFile[_MAX_PATH+1];
6251 char szNewPath[_MAX_PATH+1];
6252 char *pszFilePart;
6253 HANDLE hf;
6254#ifdef FEAT_MBYTE
6255 WCHAR *wold = NULL;
6256 WCHAR *wnew = NULL;
6257 int retval = -1;
6258
6259 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6260 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006261 wold = enc_to_utf16((char_u *)pszOldFile, NULL);
6262 wnew = enc_to_utf16((char_u *)pszNewFile, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006263 if (wold != NULL && wnew != NULL)
6264 retval = mch_wrename(wold, wnew);
6265 vim_free(wold);
6266 vim_free(wnew);
6267 if (retval == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
6268 return retval;
6269 /* Retry with non-wide function (for Windows 98). */
6270 }
6271#endif
6272
6273 /*
6274 * No need to play tricks if not running Windows 95, unless the file name
6275 * contains a "~" as the seventh character.
6276 */
6277 if (!mch_windows95())
6278 {
6279 pszFilePart = (char *)gettail((char_u *)pszOldFile);
6280 if (STRLEN(pszFilePart) < 8 || pszFilePart[6] != '~')
6281 return rename(pszOldFile, pszNewFile);
6282 }
6283
6284 /* Get base path of new file name. Undocumented feature: If pszNewFile is
6285 * a directory, no error is returned and pszFilePart will be NULL. */
6286 if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0
6287 || pszFilePart == NULL)
6288 return -1;
6289 *pszFilePart = NUL;
6290
6291 /* Get (and create) a unique temporary file name in directory of new file */
6292 if (GetTempFileName(szNewPath, "VIM", 0, szTempFile) == 0)
6293 return -2;
6294
6295 /* blow the temp file away */
6296 if (!DeleteFile(szTempFile))
6297 return -3;
6298
6299 /* rename old file to the temp file */
6300 if (!MoveFile(pszOldFile, szTempFile))
6301 return -4;
6302
6303 /* now create an empty file called pszOldFile; this prevents the operating
6304 * system using pszOldFile as an alias (SFN) if we're renaming within the
6305 * same directory. For example, we're editing a file called
6306 * filename.asc.txt by its SFN, filena~1.txt. If we rename filena~1.txt
6307 * to filena~1.txt~ (i.e., we're making a backup while writing it), the
6308 * SFN for filena~1.txt~ will be filena~1.txt, by default, which will
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006309 * cause all sorts of problems later in buf_write(). So, we create an
6310 * empty file called filena~1.txt and the system will have to find some
6311 * other SFN for filena~1.txt~, such as filena~2.txt
Bram Moolenaar071d4272004-06-13 20:20:40 +00006312 */
6313 if ((hf = CreateFile(pszOldFile, GENERIC_WRITE, 0, NULL, CREATE_NEW,
6314 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
6315 return -5;
6316 if (!CloseHandle(hf))
6317 return -6;
6318
6319 /* rename the temp file to the new file */
6320 if (!MoveFile(szTempFile, pszNewFile))
6321 {
6322 /* Renaming failed. Rename the file back to its old name, so that it
6323 * looks like nothing happened. */
6324 (void)MoveFile(szTempFile, pszOldFile);
6325
6326 return -7;
6327 }
6328
6329 /* Seems to be left around on Novell filesystems */
6330 DeleteFile(szTempFile);
6331
6332 /* finally, remove the empty old file */
6333 if (!DeleteFile(pszOldFile))
6334 return -8;
6335
6336 return 0; /* success */
6337}
6338
6339/*
6340 * Get the default shell for the current hardware platform
6341 */
6342 char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006343default_shell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006344{
6345 char* psz = NULL;
6346
6347 PlatformId();
6348
6349 if (g_PlatformId == VER_PLATFORM_WIN32_NT) /* Windows NT */
6350 psz = "cmd.exe";
6351 else if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS) /* Windows 95 */
6352 psz = "command.com";
6353
6354 return psz;
6355}
6356
6357/*
6358 * mch_access() extends access() to do more detailed check on network drives.
6359 * Returns 0 if file "n" has access rights according to "p", -1 otherwise.
6360 */
6361 int
6362mch_access(char *n, int p)
6363{
6364 HANDLE hFile;
6365 DWORD am;
6366 int retval = -1; /* default: fail */
6367#ifdef FEAT_MBYTE
6368 WCHAR *wn = NULL;
6369
6370 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006371 wn = enc_to_utf16((char_u *)n, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006372#endif
6373
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006374 if (mch_isdir((char_u *)n))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006375 {
6376 char TempName[_MAX_PATH + 16] = "";
6377#ifdef FEAT_MBYTE
6378 WCHAR TempNameW[_MAX_PATH + 16] = L"";
6379#endif
6380
6381 if (p & R_OK)
6382 {
6383 /* Read check is performed by seeing if we can do a find file on
6384 * the directory for any file. */
6385#ifdef FEAT_MBYTE
6386 if (wn != NULL)
6387 {
6388 int i;
6389 WIN32_FIND_DATAW d;
6390
6391 for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i)
6392 TempNameW[i] = wn[i];
6393 if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/')
6394 TempNameW[i++] = '\\';
6395 TempNameW[i++] = '*';
6396 TempNameW[i++] = 0;
6397
6398 hFile = FindFirstFileW(TempNameW, &d);
6399 if (hFile == INVALID_HANDLE_VALUE)
6400 {
6401 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
6402 goto getout;
6403
6404 /* Retry with non-wide function (for Windows 98). */
6405 vim_free(wn);
6406 wn = NULL;
6407 }
6408 else
6409 (void)FindClose(hFile);
6410 }
6411 if (wn == NULL)
6412#endif
6413 {
6414 char *pch;
6415 WIN32_FIND_DATA d;
6416
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006417 vim_strncpy((char_u *)TempName, (char_u *)n, _MAX_PATH);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006418 pch = TempName + STRLEN(TempName) - 1;
6419 if (*pch != '\\' && *pch != '/')
6420 *++pch = '\\';
6421 *++pch = '*';
6422 *++pch = NUL;
6423
6424 hFile = FindFirstFile(TempName, &d);
6425 if (hFile == INVALID_HANDLE_VALUE)
6426 goto getout;
6427 (void)FindClose(hFile);
6428 }
6429 }
6430
6431 if (p & W_OK)
6432 {
6433 /* Trying to create a temporary file in the directory should catch
6434 * directories on read-only network shares. However, in
6435 * directories whose ACL allows writes but denies deletes will end
6436 * up keeping the temporary file :-(. */
6437#ifdef FEAT_MBYTE
6438 if (wn != NULL)
6439 {
6440 if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW))
6441 {
6442 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
6443 goto getout;
6444
6445 /* Retry with non-wide function (for Windows 98). */
6446 vim_free(wn);
6447 wn = NULL;
6448 }
6449 else
6450 DeleteFileW(TempNameW);
6451 }
6452 if (wn == NULL)
6453#endif
6454 {
6455 if (!GetTempFileName(n, "VIM", 0, TempName))
6456 goto getout;
6457 mch_remove((char_u *)TempName);
6458 }
6459 }
6460 }
6461 else
6462 {
6463 /* Trying to open the file for the required access does ACL, read-only
6464 * network share, and file attribute checks. */
6465 am = ((p & W_OK) ? GENERIC_WRITE : 0)
6466 | ((p & R_OK) ? GENERIC_READ : 0);
6467#ifdef FEAT_MBYTE
6468 if (wn != NULL)
6469 {
6470 hFile = CreateFileW(wn, am, 0, NULL, OPEN_EXISTING, 0, NULL);
6471 if (hFile == INVALID_HANDLE_VALUE
6472 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
6473 {
6474 /* Retry with non-wide function (for Windows 98). */
6475 vim_free(wn);
6476 wn = NULL;
6477 }
6478 }
6479 if (wn == NULL)
6480#endif
6481 hFile = CreateFile(n, am, 0, NULL, OPEN_EXISTING, 0, NULL);
6482 if (hFile == INVALID_HANDLE_VALUE)
6483 goto getout;
6484 CloseHandle(hFile);
6485 }
6486
6487 retval = 0; /* success */
6488getout:
6489#ifdef FEAT_MBYTE
6490 vim_free(wn);
6491#endif
6492 return retval;
6493}
6494
6495#if defined(FEAT_MBYTE) || defined(PROTO)
6496/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006497 * Version of open() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006498 */
6499 int
6500mch_open(char *name, int flags, int mode)
6501{
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006502 /* _wopen() does not work with Borland C 5.5: creates a read-only file. */
6503# ifndef __BORLANDC__
Bram Moolenaar071d4272004-06-13 20:20:40 +00006504 WCHAR *wn;
6505 int f;
6506
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006507 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006508 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006509 wn = enc_to_utf16((char_u *)name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006510 if (wn != NULL)
6511 {
6512 f = _wopen(wn, flags, mode);
6513 vim_free(wn);
Bram Moolenaarcd981f22014-02-11 17:06:00 +01006514 if (f >= 0 || g_PlatformId == VER_PLATFORM_WIN32_NT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006515 return f;
6516 /* Retry with non-wide function (for Windows 98). Can't use
6517 * GetLastError() here and it's unclear what errno gets set to if
6518 * the _wopen() fails for missing wide functions. */
6519 }
6520 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006521# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006522
Bram Moolenaarf9e6c3b2014-11-05 18:36:03 +01006523 /* open() can open a file which name is longer than _MAX_PATH bytes
6524 * and shorter than _MAX_PATH characters successfully, but sometimes it
6525 * causes unexpected error in another part. We make it an error explicitly
6526 * here. */
6527 if (strlen(name) >= _MAX_PATH)
6528 return -1;
6529
Bram Moolenaar071d4272004-06-13 20:20:40 +00006530 return open(name, flags, mode);
6531}
6532
6533/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006534 * Version of fopen() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006535 */
6536 FILE *
6537mch_fopen(char *name, char *mode)
6538{
6539 WCHAR *wn, *wm;
6540 FILE *f = NULL;
6541
6542 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
6543# ifdef __BORLANDC__
6544 /* Wide functions of Borland C 5.5 do not work on Windows 98. */
6545 && g_PlatformId == VER_PLATFORM_WIN32_NT
6546# endif
6547 )
6548 {
Bram Moolenaare6a91fd2008-07-24 18:51:11 +00006549# if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0fde2902008-03-16 13:54:13 +00006550 /* Work around an annoying assertion in the Microsoft debug CRT
6551 * when mode's text/binary setting doesn't match _get_fmode(). */
6552 char newMode = mode[strlen(mode) - 1];
6553 int oldMode = 0;
6554
6555 _get_fmode(&oldMode);
6556 if (newMode == 't')
6557 _set_fmode(_O_TEXT);
6558 else if (newMode == 'b')
6559 _set_fmode(_O_BINARY);
6560# endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006561 wn = enc_to_utf16((char_u *)name, NULL);
6562 wm = enc_to_utf16((char_u *)mode, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006563 if (wn != NULL && wm != NULL)
6564 f = _wfopen(wn, wm);
6565 vim_free(wn);
6566 vim_free(wm);
Bram Moolenaar0fde2902008-03-16 13:54:13 +00006567
Bram Moolenaare6a91fd2008-07-24 18:51:11 +00006568# if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0fde2902008-03-16 13:54:13 +00006569 _set_fmode(oldMode);
6570# endif
6571
Bram Moolenaarcd981f22014-02-11 17:06:00 +01006572 if (f != NULL || g_PlatformId == VER_PLATFORM_WIN32_NT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006573 return f;
6574 /* Retry with non-wide function (for Windows 98). Can't use
6575 * GetLastError() here and it's unclear what errno gets set to if
6576 * the _wfopen() fails for missing wide functions. */
6577 }
6578
Bram Moolenaarf9e6c3b2014-11-05 18:36:03 +01006579 /* fopen() can open a file which name is longer than _MAX_PATH bytes
6580 * and shorter than _MAX_PATH characters successfully, but sometimes it
6581 * causes unexpected error in another part. We make it an error explicitly
6582 * here. */
6583 if (strlen(name) >= _MAX_PATH)
6584 return NULL;
6585
Bram Moolenaar071d4272004-06-13 20:20:40 +00006586 return fopen(name, mode);
6587}
6588#endif
6589
6590#ifdef FEAT_MBYTE
6591/*
6592 * SUB STREAM (aka info stream) handling:
6593 *
6594 * NTFS can have sub streams for each file. Normal contents of file is
6595 * stored in the main stream, and extra contents (author information and
6596 * title and so on) can be stored in sub stream. After Windows 2000, user
6597 * can access and store those informations in sub streams via explorer's
6598 * property menuitem in right click menu. Those informations in sub streams
6599 * were lost when copying only the main stream. So we have to copy sub
6600 * streams.
6601 *
6602 * Incomplete explanation:
6603 * http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp
6604 * More useful info and an example:
6605 * http://www.sysinternals.com/ntw2k/source/misc.shtml#streams
6606 */
6607
6608/*
6609 * Copy info stream data "substream". Read from the file with BackupRead(sh)
6610 * and write to stream "substream" of file "to".
6611 * Errors are ignored.
6612 */
6613 static void
6614copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len)
6615{
6616 HANDLE hTo;
6617 WCHAR *to_name;
6618
6619 to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR));
6620 wcscpy(to_name, to);
6621 wcscat(to_name, substream);
6622
6623 hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
6624 FILE_ATTRIBUTE_NORMAL, NULL);
6625 if (hTo != INVALID_HANDLE_VALUE)
6626 {
6627 long done;
6628 DWORD todo;
6629 DWORD readcnt, written;
6630 char buf[4096];
6631
6632 /* Copy block of bytes at a time. Abort when something goes wrong. */
6633 for (done = 0; done < len; done += written)
6634 {
6635 /* (size_t) cast for Borland C 5.5 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00006636 todo = (DWORD)((size_t)(len - done) > sizeof(buf) ? sizeof(buf)
6637 : (size_t)(len - done));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006638 if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt,
6639 FALSE, FALSE, context)
6640 || readcnt != todo
6641 || !WriteFile(hTo, buf, todo, &written, NULL)
6642 || written != todo)
6643 break;
6644 }
6645 CloseHandle(hTo);
6646 }
6647
6648 free(to_name);
6649}
6650
6651/*
6652 * Copy info streams from file "from" to file "to".
6653 */
6654 static void
6655copy_infostreams(char_u *from, char_u *to)
6656{
6657 WCHAR *fromw;
6658 WCHAR *tow;
6659 HANDLE sh;
6660 WIN32_STREAM_ID sid;
6661 int headersize;
6662 WCHAR streamname[_MAX_PATH];
6663 DWORD readcount;
6664 void *context = NULL;
6665 DWORD lo, hi;
6666 int len;
6667
6668 /* Convert the file names to wide characters. */
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006669 fromw = enc_to_utf16(from, NULL);
6670 tow = enc_to_utf16(to, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006671 if (fromw != NULL && tow != NULL)
6672 {
6673 /* Open the file for reading. */
6674 sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL,
6675 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
6676 if (sh != INVALID_HANDLE_VALUE)
6677 {
6678 /* Use BackupRead() to find the info streams. Repeat until we
6679 * have done them all.*/
6680 for (;;)
6681 {
6682 /* Get the header to find the length of the stream name. If
6683 * the "readcount" is zero we have done all info streams. */
6684 ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00006685 headersize = (int)((char *)&sid.cStreamName - (char *)&sid.dwStreamId);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006686 if (!BackupRead(sh, (LPBYTE)&sid, headersize,
6687 &readcount, FALSE, FALSE, &context)
6688 || readcount == 0)
6689 break;
6690
6691 /* We only deal with streams that have a name. The normal
6692 * file data appears to be without a name, even though docs
6693 * suggest it is called "::$DATA". */
6694 if (sid.dwStreamNameSize > 0)
6695 {
6696 /* Read the stream name. */
6697 if (!BackupRead(sh, (LPBYTE)streamname,
6698 sid.dwStreamNameSize,
6699 &readcount, FALSE, FALSE, &context))
6700 break;
6701
6702 /* Copy an info stream with a name ":anything:$DATA".
6703 * Skip "::$DATA", it has no stream name (examples suggest
6704 * it might be used for the normal file contents).
6705 * Note that BackupRead() counts bytes, but the name is in
6706 * wide characters. */
6707 len = readcount / sizeof(WCHAR);
6708 streamname[len] = 0;
6709 if (len > 7 && wcsicmp(streamname + len - 6,
6710 L":$DATA") == 0)
6711 {
6712 streamname[len - 6] = 0;
6713 copy_substream(sh, &context, tow, streamname,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00006714 (long)sid.Size.u.LowPart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006715 }
6716 }
6717
6718 /* Advance to the next stream. We might try seeking too far,
6719 * but BackupSeek() doesn't skip over stream borders, thus
6720 * that's OK. */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00006721 (void)BackupSeek(sh, sid.Size.u.LowPart, sid.Size.u.HighPart,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006722 &lo, &hi, &context);
6723 }
6724
6725 /* Clear the context. */
6726 (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context);
6727
6728 CloseHandle(sh);
6729 }
6730 }
6731 vim_free(fromw);
6732 vim_free(tow);
6733}
6734#endif
6735
6736/*
6737 * Copy file attributes from file "from" to file "to".
6738 * For Windows NT and later we copy info streams.
6739 * Always returns zero, errors are ignored.
6740 */
6741 int
6742mch_copy_file_attribute(char_u *from, char_u *to)
6743{
6744#ifdef FEAT_MBYTE
6745 /* File streams only work on Windows NT and later. */
6746 PlatformId();
6747 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
6748 copy_infostreams(from, to);
6749#endif
6750 return 0;
6751}
6752
6753#if defined(MYRESETSTKOFLW) || defined(PROTO)
6754/*
6755 * Recreate a destroyed stack guard page in win32.
6756 * Written by Benjamin Peterson.
6757 */
6758
6759/* These magic numbers are from the MS header files */
6760#define MIN_STACK_WIN9X 17
6761#define MIN_STACK_WINNT 2
6762
6763/*
6764 * This function does the same thing as _resetstkoflw(), which is only
6765 * available in DevStudio .net and later.
6766 * Returns 0 for failure, 1 for success.
6767 */
6768 int
6769myresetstkoflw(void)
6770{
6771 BYTE *pStackPtr;
6772 BYTE *pGuardPage;
6773 BYTE *pStackBase;
6774 BYTE *pLowestPossiblePage;
6775 MEMORY_BASIC_INFORMATION mbi;
6776 SYSTEM_INFO si;
6777 DWORD nPageSize;
6778 DWORD dummy;
6779
6780 /* This code will not work on win32s. */
6781 PlatformId();
6782 if (g_PlatformId == VER_PLATFORM_WIN32s)
6783 return 0;
6784
6785 /* We need to know the system page size. */
6786 GetSystemInfo(&si);
6787 nPageSize = si.dwPageSize;
6788
6789 /* ...and the current stack pointer */
6790 pStackPtr = (BYTE*)_alloca(1);
6791
6792 /* ...and the base of the stack. */
6793 if (VirtualQuery(pStackPtr, &mbi, sizeof mbi) == 0)
6794 return 0;
6795 pStackBase = (BYTE*)mbi.AllocationBase;
6796
6797 /* ...and the page thats min_stack_req pages away from stack base; this is
6798 * the lowest page we could use. */
6799 pLowestPossiblePage = pStackBase + ((g_PlatformId == VER_PLATFORM_WIN32_NT)
6800 ? MIN_STACK_WINNT : MIN_STACK_WIN9X) * nPageSize;
6801
6802 /* On Win95, we want the next page down from the end of the stack. */
6803 if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS)
6804 {
6805 /* Find the page that's only 1 page down from the page that the stack
6806 * ptr is in. */
6807 pGuardPage = (BYTE*)((DWORD)nPageSize * (((DWORD)pStackPtr
6808 / (DWORD)nPageSize) - 1));
6809 if (pGuardPage < pLowestPossiblePage)
6810 return 0;
6811
6812 /* Apply the noaccess attribute to the page -- there's no guard
6813 * attribute in win95-type OSes. */
6814 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_NOACCESS, &dummy))
6815 return 0;
6816 }
6817 else
6818 {
6819 /* On NT, however, we want the first committed page in the stack Start
6820 * at the stack base and move forward through memory until we find a
6821 * committed block. */
6822 BYTE *pBlock = pStackBase;
6823
Bram Moolenaara466c992005-07-09 21:03:22 +00006824 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006825 {
6826 if (VirtualQuery(pBlock, &mbi, sizeof mbi) == 0)
6827 return 0;
6828
6829 pBlock += mbi.RegionSize;
6830
6831 if (mbi.State & MEM_COMMIT)
6832 break;
6833 }
6834
6835 /* mbi now describes the first committed block in the stack. */
6836 if (mbi.Protect & PAGE_GUARD)
6837 return 1;
6838
6839 /* decide where the guard page should start */
6840 if ((long_u)(mbi.BaseAddress) < (long_u)pLowestPossiblePage)
6841 pGuardPage = pLowestPossiblePage;
6842 else
6843 pGuardPage = (BYTE*)mbi.BaseAddress;
6844
6845 /* allocate the guard page */
6846 if (!VirtualAlloc(pGuardPage, nPageSize, MEM_COMMIT, PAGE_READWRITE))
6847 return 0;
6848
6849 /* apply the guard attribute to the page */
6850 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_READWRITE | PAGE_GUARD,
6851 &dummy))
6852 return 0;
6853 }
6854
6855 return 1;
6856}
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006857#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006858
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006859
6860#if defined(FEAT_MBYTE) || defined(PROTO)
6861/*
6862 * The command line arguments in UCS2
6863 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00006864static int nArgsW = 0;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006865static LPWSTR *ArglistW = NULL;
6866static int global_argc = 0;
6867static char **global_argv;
6868
6869static int used_file_argc = 0; /* last argument in global_argv[] used
6870 for the argument list. */
6871static int *used_file_indexes = NULL; /* indexes in global_argv[] for
6872 command line arguments added to
6873 the argument list */
6874static int used_file_count = 0; /* nr of entries in used_file_indexes */
6875static int used_file_literal = FALSE; /* take file names literally */
6876static int used_file_full_path = FALSE; /* file name was full path */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006877static int used_file_diff_mode = FALSE; /* file name was with diff mode */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006878static int used_alist_count = 0;
6879
6880
6881/*
6882 * Get the command line arguments. Unicode version.
6883 * Returns argc. Zero when something fails.
6884 */
6885 int
6886get_cmd_argsW(char ***argvp)
6887{
6888 char **argv = NULL;
6889 int argc = 0;
6890 int i;
6891
Bram Moolenaar14993322014-09-09 12:25:33 +02006892 free_cmd_argsW();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006893 ArglistW = CommandLineToArgvW(GetCommandLineW(), &nArgsW);
6894 if (ArglistW != NULL)
6895 {
6896 argv = malloc((nArgsW + 1) * sizeof(char *));
6897 if (argv != NULL)
6898 {
6899 argc = nArgsW;
6900 argv[argc] = NULL;
6901 for (i = 0; i < argc; ++i)
6902 {
6903 int len;
6904
6905 /* Convert each Unicode argument to the current codepage. */
6906 WideCharToMultiByte_alloc(GetACP(), 0,
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00006907 ArglistW[i], (int)wcslen(ArglistW[i]) + 1,
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006908 (LPSTR *)&argv[i], &len, 0, 0);
6909 if (argv[i] == NULL)
6910 {
6911 /* Out of memory, clear everything. */
6912 while (i > 0)
6913 free(argv[--i]);
6914 free(argv);
Bram Moolenaar73c61632013-12-07 14:48:10 +01006915 argv = NULL;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006916 argc = 0;
6917 }
6918 }
6919 }
6920 }
6921
6922 global_argc = argc;
6923 global_argv = argv;
6924 if (argc > 0)
Bram Moolenaar14993322014-09-09 12:25:33 +02006925 {
6926 if (used_file_indexes != NULL)
6927 free(used_file_indexes);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006928 used_file_indexes = malloc(argc * sizeof(int));
Bram Moolenaar14993322014-09-09 12:25:33 +02006929 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006930
6931 if (argvp != NULL)
6932 *argvp = argv;
6933 return argc;
6934}
6935
6936 void
6937free_cmd_argsW(void)
6938{
6939 if (ArglistW != NULL)
6940 {
6941 GlobalFree(ArglistW);
6942 ArglistW = NULL;
6943 }
6944}
6945
6946/*
6947 * Remember "name" is an argument that was added to the argument list.
6948 * This avoids that we have to re-parse the argument list when fix_arg_enc()
6949 * is called.
6950 */
6951 void
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006952used_file_arg(char *name, int literal, int full_path, int diff_mode)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006953{
6954 int i;
6955
6956 if (used_file_indexes == NULL)
6957 return;
6958 for (i = used_file_argc + 1; i < global_argc; ++i)
6959 if (STRCMP(global_argv[i], name) == 0)
6960 {
6961 used_file_argc = i;
6962 used_file_indexes[used_file_count++] = i;
6963 break;
6964 }
6965 used_file_literal = literal;
6966 used_file_full_path = full_path;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006967 used_file_diff_mode = diff_mode;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006968}
6969
6970/*
6971 * Remember the length of the argument list as it was. If it changes then we
6972 * leave it alone when 'encoding' is set.
6973 */
6974 void
6975set_alist_count(void)
6976{
6977 used_alist_count = GARGCOUNT;
6978}
6979
6980/*
6981 * Fix the encoding of the command line arguments. Invoked when 'encoding'
6982 * has been changed while starting up. Use the UCS-2 command line arguments
6983 * and convert them to 'encoding'.
6984 */
6985 void
6986fix_arg_enc(void)
6987{
6988 int i;
6989 int idx;
6990 char_u *str;
Bram Moolenaar86b68352004-12-27 21:59:20 +00006991 int *fnum_list;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006992
6993 /* Safety checks:
6994 * - if argument count differs between the wide and non-wide argument
6995 * list, something must be wrong.
6996 * - the file name arguments must have been located.
6997 * - the length of the argument list wasn't changed by the user.
6998 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00006999 if (global_argc != nArgsW
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007000 || ArglistW == NULL
7001 || used_file_indexes == NULL
7002 || used_file_count == 0
7003 || used_alist_count != GARGCOUNT)
7004 return;
7005
Bram Moolenaar86b68352004-12-27 21:59:20 +00007006 /* Remember the buffer numbers for the arguments. */
7007 fnum_list = (int *)alloc((int)sizeof(int) * GARGCOUNT);
7008 if (fnum_list == NULL)
7009 return; /* out of memory */
7010 for (i = 0; i < GARGCOUNT; ++i)
7011 fnum_list[i] = GARGLIST[i].ae_fnum;
7012
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007013 /* Clear the argument list. Make room for the new arguments. */
7014 alist_clear(&global_alist);
7015 if (ga_grow(&global_alist.al_ga, used_file_count) == FAIL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00007016 return; /* out of memory */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007017
7018 for (i = 0; i < used_file_count; ++i)
7019 {
7020 idx = used_file_indexes[i];
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007021 str = utf16_to_enc(ArglistW[idx], NULL);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007022 if (str != NULL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00007023 {
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007024#ifdef FEAT_DIFF
7025 /* When using diff mode may need to concatenate file name to
7026 * directory name. Just like it's done in main(). */
7027 if (used_file_diff_mode && mch_isdir(str) && GARGCOUNT > 0
7028 && !mch_isdir(alist_name(&GARGLIST[0])))
7029 {
7030 char_u *r;
7031
7032 r = concat_fnames(str, gettail(alist_name(&GARGLIST[0])), TRUE);
7033 if (r != NULL)
7034 {
7035 vim_free(str);
7036 str = r;
7037 }
7038 }
7039#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00007040 /* Re-use the old buffer by renaming it. When not using literal
7041 * names it's done by alist_expand() below. */
7042 if (used_file_literal)
7043 buf_set_name(fnum_list[i], str);
7044
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007045 alist_add(&global_alist, str, used_file_literal ? 2 : 0);
Bram Moolenaar86b68352004-12-27 21:59:20 +00007046 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007047 }
7048
7049 if (!used_file_literal)
7050 {
7051 /* Now expand wildcards in the arguments. */
7052 /* Temporarily add '(' and ')' to 'isfname'. These are valid
7053 * filename characters but are excluded from 'isfname' to make
7054 * "gf" work on a file name in parenthesis (e.g.: see vim.h). */
7055 do_cmdline_cmd((char_u *)":let SaVe_ISF = &isf|set isf+=(,)");
Bram Moolenaar86b68352004-12-27 21:59:20 +00007056 alist_expand(fnum_list, used_alist_count);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007057 do_cmdline_cmd((char_u *)":let &isf = SaVe_ISF|unlet SaVe_ISF");
7058 }
7059
7060 /* If wildcard expansion failed, we are editing the first file of the
7061 * arglist and there is no file name: Edit the first argument now. */
7062 if (curwin->w_arg_idx == 0 && curbuf->b_fname == NULL)
7063 {
7064 do_cmdline_cmd((char_u *)":rewind");
7065 if (GARGCOUNT == 1 && used_file_full_path)
7066 (void)vim_chdirfile(alist_name(&GARGLIST[0]));
7067 }
Bram Moolenaar86b68352004-12-27 21:59:20 +00007068
7069 set_alist_count();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007070}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007071#endif