blob: 816c3a59cf91decd528f3fd341c7121a2e283561 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
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
Bram Moolenaarfb630902016-10-29 14:55:00 +020053#ifdef FEAT_JOB_CHANNEL
54# include <tlhelp32.h>
55#endif
56
Bram Moolenaar071d4272004-06-13 20:20:40 +000057#ifdef __MINGW32__
58# ifndef FROM_LEFT_1ST_BUTTON_PRESSED
59# define FROM_LEFT_1ST_BUTTON_PRESSED 0x0001
60# endif
61# ifndef RIGHTMOST_BUTTON_PRESSED
62# define RIGHTMOST_BUTTON_PRESSED 0x0002
63# endif
64# ifndef FROM_LEFT_2ND_BUTTON_PRESSED
65# define FROM_LEFT_2ND_BUTTON_PRESSED 0x0004
66# endif
67# ifndef FROM_LEFT_3RD_BUTTON_PRESSED
68# define FROM_LEFT_3RD_BUTTON_PRESSED 0x0008
69# endif
70# ifndef FROM_LEFT_4TH_BUTTON_PRESSED
71# define FROM_LEFT_4TH_BUTTON_PRESSED 0x0010
72# endif
73
74/*
75 * EventFlags
76 */
77# ifndef MOUSE_MOVED
78# define MOUSE_MOVED 0x0001
79# endif
80# ifndef DOUBLE_CLICK
81# define DOUBLE_CLICK 0x0002
82# endif
83#endif
84
85/* Record all output and all keyboard & mouse input */
86/* #define MCH_WRITE_DUMP */
87
88#ifdef MCH_WRITE_DUMP
89FILE* fdDump = NULL;
90#endif
91
92/*
93 * When generating prototypes for Win32 on Unix, these lines make the syntax
94 * errors disappear. They do not need to be correct.
95 */
96#ifdef PROTO
97#define WINAPI
Bram Moolenaar071d4272004-06-13 20:20:40 +000098typedef char * LPCSTR;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +000099typedef char * LPWSTR;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000100typedef int ACCESS_MASK;
101typedef int BOOL;
102typedef int COLORREF;
103typedef int CONSOLE_CURSOR_INFO;
104typedef int COORD;
105typedef int DWORD;
106typedef int HANDLE;
Bram Moolenaaref269542016-01-19 13:22:12 +0100107typedef int LPHANDLE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000108typedef int HDC;
109typedef int HFONT;
110typedef int HICON;
111typedef int HINSTANCE;
112typedef int HWND;
113typedef int INPUT_RECORD;
Bram Moolenaare0ab9792017-08-02 23:18:25 +0200114typedef int INT;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000115typedef int KEY_EVENT_RECORD;
116typedef int LOGFONT;
117typedef int LPBOOL;
118typedef int LPCTSTR;
119typedef int LPDWORD;
120typedef int LPSTR;
121typedef int LPTSTR;
122typedef int LPVOID;
123typedef int MOUSE_EVENT_RECORD;
124typedef int PACL;
125typedef int PDWORD;
126typedef int PHANDLE;
127typedef int PRINTDLG;
128typedef int PSECURITY_DESCRIPTOR;
129typedef int PSID;
130typedef int SECURITY_INFORMATION;
131typedef int SHORT;
132typedef int SMALL_RECT;
133typedef int TEXTMETRIC;
134typedef int TOKEN_INFORMATION_CLASS;
135typedef int TRUSTEE;
136typedef int WORD;
137typedef int WCHAR;
138typedef void VOID;
Bram Moolenaar82881492012-11-20 16:53:39 +0100139typedef int BY_HANDLE_FILE_INFORMATION;
Bram Moolenaar32ac8cd2013-07-03 18:49:17 +0200140typedef int SE_OBJECT_TYPE;
141typedef int PSNSECINFO;
142typedef int PSNSECINFOW;
Bram Moolenaarb8e0bdb2014-11-12 16:10:48 +0100143typedef int STARTUPINFO;
144typedef int PROCESS_INFORMATION;
Bram Moolenaard90b6c02016-08-28 18:10:45 +0200145typedef int LPSECURITY_ATTRIBUTES;
146# define __stdcall /* empty */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000147#endif
148
Bram Moolenaar071d4272004-06-13 20:20:40 +0000149#if defined(__BORLANDC__)
150/* Strangely Borland uses a non-standard name. */
151# define wcsicmp(a, b) wcscmpi((a), (b))
152#endif
153
154#ifndef FEAT_GUI_W32
155/* Win32 Console handles for input and output */
156static HANDLE g_hConIn = INVALID_HANDLE_VALUE;
157static HANDLE g_hConOut = INVALID_HANDLE_VALUE;
158
159/* Win32 Screen buffer,coordinate,console I/O information */
160static SMALL_RECT g_srScrollRegion;
161static COORD g_coord; /* 0-based, but external coords are 1-based */
162
163/* The attribute of the screen when the editor was started */
164static WORD g_attrDefault = 7; /* lightgray text on black background */
165static WORD g_attrCurrent;
166
167static int g_fCBrkPressed = FALSE; /* set by ctrl-break interrupt */
168static int g_fCtrlCPressed = FALSE; /* set when ctrl-C or ctrl-break detected */
169static int g_fForceExit = FALSE; /* set when forcefully exiting */
170
Bram Moolenaar071d4272004-06-13 20:20:40 +0000171static void scroll(unsigned cLines);
172static void set_scroll_region(unsigned left, unsigned top,
173 unsigned right, unsigned bottom);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000174static void delete_lines(unsigned cLines);
175static void gotoxy(unsigned x, unsigned y);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000176static void standout(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000177static int s_cursor_visible = TRUE;
178static int did_create_conin = FALSE;
179#else
180static int s_dont_use_vimrun = TRUE;
181static int need_vimrun_warning = FALSE;
182static char *vimrun_path = "vimrun ";
183#endif
184
Bram Moolenaar12b559e2013-06-12 22:41:37 +0200185static int win32_getattrs(char_u *name);
186static int win32_setattrs(char_u *name, int attrs);
187static int win32_set_archive(char_u *name);
188
Bram Moolenaard9ef1b82019-02-13 19:23:10 +0100189static int conpty_working = 0;
190static int conpty_stable = 0;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +0100191static void vtp_flag_init();
192
193#ifndef FEAT_GUI_W32
Bram Moolenaar6902c0e2019-02-16 14:07:37 +0100194static int vtp_working = 0;
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100195static void vtp_init();
196static void vtp_exit();
197static int vtp_printf(char *format, ...);
198static void vtp_sgr_bulk(int arg);
199static void vtp_sgr_bulks(int argc, int *argv);
200
201static guicolor_T save_console_bg_rgb;
202static guicolor_T save_console_fg_rgb;
203
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +0200204static int g_color_index_bg = 0;
205static int g_color_index_fg = 7;
206
207# ifdef FEAT_TERMGUICOLORS
208static int default_console_color_bg = 0x000000; // black
209static int default_console_color_fg = 0xc0c0c0; // white
210# endif
211
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100212# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarc5cd8852018-05-01 15:47:38 +0200213# define USE_VTP (vtp_working && is_term_win32() && (p_tgc || (!p_tgc && t_colors >= 256)))
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100214# else
215# define USE_VTP 0
216# endif
217
218static void set_console_color_rgb(void);
219static void reset_console_color_rgb(void);
220#endif
221
222/* This flag is newly created from Windows 10 */
223#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
224# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
225#endif
226
227#ifndef FEAT_GUI_W32
Bram Moolenaar071d4272004-06-13 20:20:40 +0000228static int suppress_winsize = 1; /* don't fiddle with console */
229#endif
230
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200231static char_u *exe_path = NULL;
232
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100233static BOOL win8_or_later = FALSE;
234
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100235#ifndef FEAT_GUI_W32
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100236/* Dynamic loading for portability */
237typedef struct _DYN_CONSOLE_SCREEN_BUFFER_INFOEX
238{
239 ULONG cbSize;
240 COORD dwSize;
241 COORD dwCursorPosition;
242 WORD wAttributes;
243 SMALL_RECT srWindow;
244 COORD dwMaximumWindowSize;
245 WORD wPopupAttributes;
246 BOOL bFullscreenSupported;
247 COLORREF ColorTable[16];
248} DYN_CONSOLE_SCREEN_BUFFER_INFOEX, *PDYN_CONSOLE_SCREEN_BUFFER_INFOEX;
249typedef BOOL (WINAPI *PfnGetConsoleScreenBufferInfoEx)(HANDLE, PDYN_CONSOLE_SCREEN_BUFFER_INFOEX);
250static PfnGetConsoleScreenBufferInfoEx pGetConsoleScreenBufferInfoEx;
251typedef BOOL (WINAPI *PfnSetConsoleScreenBufferInfoEx)(HANDLE, PDYN_CONSOLE_SCREEN_BUFFER_INFOEX);
252static PfnSetConsoleScreenBufferInfoEx pSetConsoleScreenBufferInfoEx;
253static BOOL has_csbiex = FALSE;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +0100254#endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100255
256/*
257 * Get version number including build number
258 */
259typedef BOOL (WINAPI *PfnRtlGetVersion)(LPOSVERSIONINFOW);
260# define MAKE_VER(major, minor, build) \
261 (((major) << 24) | ((minor) << 16) | (build))
262
263 static DWORD
264get_build_number(void)
265{
266 OSVERSIONINFOW osver = {sizeof(OSVERSIONINFOW)};
267 HMODULE hNtdll;
268 PfnRtlGetVersion pRtlGetVersion;
269 DWORD ver = MAKE_VER(0, 0, 0);
270
271 hNtdll = GetModuleHandle("ntdll.dll");
272 if (hNtdll != NULL)
273 {
274 pRtlGetVersion =
275 (PfnRtlGetVersion)GetProcAddress(hNtdll, "RtlGetVersion");
276 pRtlGetVersion(&osver);
277 ver = MAKE_VER(min(osver.dwMajorVersion, 255),
278 min(osver.dwMinorVersion, 255),
279 min(osver.dwBuildNumber, 32767));
280 }
281 return ver;
282}
283
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +0100284#ifndef FEAT_GUI_W32
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100285/*
286 * Version of ReadConsoleInput() that works with IME.
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100287 * Works around problems on Windows 8.
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100288 */
289 static BOOL
290read_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100291 HANDLE hInput,
292 INPUT_RECORD *lpBuffer,
293 DWORD nLength,
294 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100295{
296 enum
297 {
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100298 IRSIZE = 10
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100299 };
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100300 static INPUT_RECORD s_irCache[IRSIZE];
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100301 static DWORD s_dwIndex = 0;
302 static DWORD s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100303 DWORD dwEvents;
Bram Moolenaardd415a62014-02-05 14:02:27 +0100304 int head;
305 int tail;
306 int i;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100307
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200308 if (nLength == -2)
309 return (s_dwMax > 0) ? TRUE : FALSE;
310
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100311 if (!win8_or_later)
312 {
313 if (nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200314 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
315 return ReadConsoleInputW(hInput, lpBuffer, 1, &dwEvents);
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100316 }
317
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100318 if (s_dwMax == 0)
319 {
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100320 if (nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200321 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
322 if (!ReadConsoleInputW(hInput, s_irCache, IRSIZE, &dwEvents))
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100323 return FALSE;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100324 s_dwIndex = 0;
325 s_dwMax = dwEvents;
326 if (dwEvents == 0)
327 {
328 *lpEvents = 0;
329 return TRUE;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100330 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100331
332 if (s_dwMax > 1)
333 {
334 head = 0;
335 tail = s_dwMax - 1;
336 while (head != tail)
337 {
338 if (s_irCache[head].EventType == WINDOW_BUFFER_SIZE_EVENT
339 && s_irCache[head + 1].EventType
340 == WINDOW_BUFFER_SIZE_EVENT)
341 {
342 /* Remove duplicate event to avoid flicker. */
343 for (i = head; i < tail; ++i)
344 s_irCache[i] = s_irCache[i + 1];
345 --tail;
346 continue;
347 }
348 head++;
349 }
350 s_dwMax = tail + 1;
351 }
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100352 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100353
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100354 *lpBuffer = s_irCache[s_dwIndex];
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200355 if (!(nLength == -1 || nLength == -2) && ++s_dwIndex >= s_dwMax)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100356 s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100357 *lpEvents = 1;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100358 return TRUE;
359}
360
361/*
362 * Version of PeekConsoleInput() that works with IME.
363 */
364 static BOOL
365peek_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100366 HANDLE hInput,
367 INPUT_RECORD *lpBuffer,
368 DWORD nLength,
369 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100370{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100371 return read_console_input(hInput, lpBuffer, -1, lpEvents);
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100372}
373
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100374# ifdef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200375 static DWORD
376msg_wait_for_multiple_objects(
377 DWORD nCount,
378 LPHANDLE pHandles,
379 BOOL fWaitAll,
380 DWORD dwMilliseconds,
381 DWORD dwWakeMask)
382{
383 if (read_console_input(NULL, NULL, -2, NULL))
384 return WAIT_OBJECT_0;
385 return MsgWaitForMultipleObjects(nCount, pHandles, fWaitAll,
386 dwMilliseconds, dwWakeMask);
387}
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100388# endif
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200389
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100390# ifndef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200391 static DWORD
392wait_for_single_object(
393 HANDLE hHandle,
394 DWORD dwMilliseconds)
395{
396 if (read_console_input(NULL, NULL, -2, NULL))
397 return WAIT_OBJECT_0;
398 return WaitForSingleObject(hHandle, dwMilliseconds);
399}
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100400# endif
401#endif
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200402
Bram Moolenaar071d4272004-06-13 20:20:40 +0000403 static void
404get_exe_name(void)
405{
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100406 /* Maximum length of $PATH is more than MAXPATHL. 8191 is often mentioned
407 * as the maximum length that works (plus a NUL byte). */
408#define MAX_ENV_PATH_LEN 8192
409 char temp[MAX_ENV_PATH_LEN];
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200410 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000411
412 if (exe_name == NULL)
413 {
414 /* store the name of the executable, may be used for $VIM */
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100415 GetModuleFileName(NULL, temp, MAX_ENV_PATH_LEN - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000416 if (*temp != NUL)
417 exe_name = FullName_save((char_u *)temp, FALSE);
418 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000419
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200420 if (exe_path == NULL && exe_name != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000421 {
Bram Moolenaar6b5ef062010-10-27 12:18:00 +0200422 exe_path = vim_strnsave(exe_name,
423 (int)(gettail_sep(exe_name) - exe_name));
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200424 if (exe_path != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000425 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200426 /* Append our starting directory to $PATH, so that when doing
427 * "!xxd" it's found in our starting directory. Needed because
428 * SearchPath() also looks there. */
429 p = mch_getenv("PATH");
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100430 if (p == NULL
431 || STRLEN(p) + STRLEN(exe_path) + 2 < MAX_ENV_PATH_LEN)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200432 {
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100433 if (p == NULL || *p == NUL)
434 temp[0] = NUL;
435 else
436 {
437 STRCPY(temp, p);
438 STRCAT(temp, ";");
439 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200440 STRCAT(temp, exe_path);
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100441 vim_setenv((char_u *)"PATH", (char_u *)temp);
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200442 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000443 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000444 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445}
446
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200447/*
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100448 * Unescape characters in "p" that appear in "escaped".
449 */
450 static void
451unescape_shellxquote(char_u *p, char_u *escaped)
452{
Bram Moolenaar4336cdf2012-02-29 13:58:47 +0100453 int l = (int)STRLEN(p);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100454 int n;
455
456 while (*p != NUL)
457 {
458 if (*p == '^' && vim_strchr(escaped, p[1]) != NULL)
459 mch_memmove(p, p + 1, l--);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100460 n = (*mb_ptr2len)(p);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100461 p += n;
462 l -= n;
463 }
464}
465
466/*
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200467 * Load library "name".
468 */
469 HINSTANCE
470vimLoadLib(char *name)
471{
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200472 HINSTANCE dll = NULL;
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200473
Bram Moolenaarfaca8402012-10-21 02:37:10 +0200474 /* NOTE: Do not use mch_dirname() and mch_chdir() here, they may call
475 * vimLoadLib() recursively, which causes a stack overflow. */
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200476 if (exe_path == NULL)
477 get_exe_name();
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200478 if (exe_path != NULL)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200479 {
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200480 WCHAR old_dirw[MAXPATHL];
481
482 if (GetCurrentDirectoryW(MAXPATHL, old_dirw) != 0)
483 {
484 /* Change directory to where the executable is, both to make
485 * sure we find a .dll there and to avoid looking for a .dll
486 * in the current directory. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100487 SetCurrentDirectory((LPCSTR)exe_path);
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200488 dll = LoadLibrary(name);
489 SetCurrentDirectoryW(old_dirw);
490 return dll;
491 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200492 }
493 return dll;
494}
495
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100496#if defined(DYNAMIC_ICONV) || defined(DYNAMIC_GETTEXT) || defined(PROTO)
497/*
498 * Get related information about 'funcname' which is imported by 'hInst'.
499 * If 'info' is 0, return the function address.
500 * If 'info' is 1, return the module name which the function is imported from.
501 */
502 static void *
503get_imported_func_info(HINSTANCE hInst, const char *funcname, int info)
504{
505 PBYTE pImage = (PBYTE)hInst;
506 PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)hInst;
507 PIMAGE_NT_HEADERS pPE;
508 PIMAGE_IMPORT_DESCRIPTOR pImpDesc;
509 PIMAGE_THUNK_DATA pIAT; /* Import Address Table */
510 PIMAGE_THUNK_DATA pINT; /* Import Name Table */
511 PIMAGE_IMPORT_BY_NAME pImpName;
512
513 if (pDOS->e_magic != IMAGE_DOS_SIGNATURE)
514 return NULL;
515 pPE = (PIMAGE_NT_HEADERS)(pImage + pDOS->e_lfanew);
516 if (pPE->Signature != IMAGE_NT_SIGNATURE)
517 return NULL;
518 pImpDesc = (PIMAGE_IMPORT_DESCRIPTOR)(pImage
519 + pPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
520 .VirtualAddress);
521 for (; pImpDesc->FirstThunk; ++pImpDesc)
522 {
523 if (!pImpDesc->OriginalFirstThunk)
524 continue;
525 pIAT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->FirstThunk);
526 pINT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->OriginalFirstThunk);
527 for (; pIAT->u1.Function; ++pIAT, ++pINT)
528 {
529 if (IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal))
530 continue;
531 pImpName = (PIMAGE_IMPORT_BY_NAME)(pImage
532 + (UINT_PTR)(pINT->u1.AddressOfData));
533 if (strcmp((char *)pImpName->Name, funcname) == 0)
534 {
535 switch (info)
536 {
537 case 0:
538 return (void *)pIAT->u1.Function;
539 case 1:
540 return (void *)(pImage + pImpDesc->Name);
541 default:
542 return NULL;
543 }
544 }
545 }
546 }
547 return NULL;
548}
549
550/*
551 * Get the module handle which 'funcname' in 'hInst' is imported from.
552 */
553 HINSTANCE
554find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname)
555{
556 char *modulename;
557
558 modulename = (char *)get_imported_func_info(hInst, funcname, 1);
559 if (modulename != NULL)
560 return GetModuleHandleA(modulename);
561 return NULL;
562}
563
564/*
565 * Get the address of 'funcname' which is imported by 'hInst' DLL.
566 */
567 void *
568get_dll_import_func(HINSTANCE hInst, const char *funcname)
569{
570 return get_imported_func_info(hInst, funcname, 0);
571}
572#endif
573
Bram Moolenaar071d4272004-06-13 20:20:40 +0000574#if defined(DYNAMIC_GETTEXT) || defined(PROTO)
575# ifndef GETTEXT_DLL
576# define GETTEXT_DLL "libintl.dll"
Bram Moolenaar7554c542018-10-06 15:03:15 +0200577# define GETTEXT_DLL_ALT1 "libintl-8.dll"
578# define GETTEXT_DLL_ALT2 "intl.dll"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000579# endif
Bram Moolenaarf6a2b082012-06-29 13:14:03 +0200580/* Dummy functions */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000581static char *null_libintl_gettext(const char *);
Bram Moolenaaree695f72016-08-03 22:08:45 +0200582static char *null_libintl_ngettext(const char *, const char *, unsigned long n);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000583static char *null_libintl_textdomain(const char *);
584static char *null_libintl_bindtextdomain(const char *, const char *);
585static char *null_libintl_bind_textdomain_codeset(const char *, const char *);
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100586static int null_libintl_putenv(const char *);
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100587static int null_libintl_wputenv(const wchar_t *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000588
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200589static HINSTANCE hLibintlDLL = NULL;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000590char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
Bram Moolenaaree695f72016-08-03 22:08:45 +0200591char *(*dyn_libintl_ngettext)(const char *, const char *, unsigned long n)
592 = null_libintl_ngettext;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000593char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
594char *(*dyn_libintl_bindtextdomain)(const char *, const char *)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000595 = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000596char *(*dyn_libintl_bind_textdomain_codeset)(const char *, const char *)
597 = null_libintl_bind_textdomain_codeset;
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100598int (*dyn_libintl_putenv)(const char *) = null_libintl_putenv;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100599int (*dyn_libintl_wputenv)(const wchar_t *) = null_libintl_wputenv;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000600
601 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100602dyn_libintl_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603{
604 int i;
605 static struct
606 {
607 char *name;
608 FARPROC *ptr;
609 } libintl_entry[] =
610 {
611 {"gettext", (FARPROC*)&dyn_libintl_gettext},
Bram Moolenaaree695f72016-08-03 22:08:45 +0200612 {"ngettext", (FARPROC*)&dyn_libintl_ngettext},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000613 {"textdomain", (FARPROC*)&dyn_libintl_textdomain},
614 {"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
615 {NULL, NULL}
616 };
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100617 HINSTANCE hmsvcrt;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618
Bram Moolenaar7554c542018-10-06 15:03:15 +0200619 // No need to initialize twice.
620 if (hLibintlDLL != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621 return 1;
Bram Moolenaar7554c542018-10-06 15:03:15 +0200622 // Load gettext library (libintl.dll and other names).
Bram Moolenaar923e43b2016-01-28 15:07:38 +0100623 hLibintlDLL = vimLoadLib(GETTEXT_DLL);
Bram Moolenaar7554c542018-10-06 15:03:15 +0200624#ifdef GETTEXT_DLL_ALT1
Bram Moolenaar286eacd2016-01-16 18:05:50 +0100625 if (!hLibintlDLL)
Bram Moolenaar7554c542018-10-06 15:03:15 +0200626 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT1);
627#endif
628#ifdef GETTEXT_DLL_ALT2
629 if (!hLibintlDLL)
630 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT2);
Bram Moolenaar938ee832016-01-24 15:36:03 +0100631#endif
632 if (!hLibintlDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000633 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200634 if (p_verbose > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000635 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200636 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100637 semsg(_(e_loadlib), GETTEXT_DLL);
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200638 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000639 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200640 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641 }
642 for (i = 0; libintl_entry[i].name != NULL
643 && libintl_entry[i].ptr != NULL; ++i)
644 {
645 if ((*libintl_entry[i].ptr = (FARPROC)GetProcAddress(hLibintlDLL,
646 libintl_entry[i].name)) == NULL)
647 {
648 dyn_libintl_end();
649 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000650 {
651 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100652 semsg(_(e_loadfunc), libintl_entry[i].name);
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000653 verbose_leave();
654 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000655 return 0;
656 }
657 }
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000658
659 /* The bind_textdomain_codeset() function is optional. */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000660 dyn_libintl_bind_textdomain_codeset = (void *)GetProcAddress(hLibintlDLL,
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000661 "bind_textdomain_codeset");
662 if (dyn_libintl_bind_textdomain_codeset == NULL)
663 dyn_libintl_bind_textdomain_codeset =
664 null_libintl_bind_textdomain_codeset;
665
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100666 /* _putenv() function for the libintl.dll is optional. */
667 hmsvcrt = find_imported_module_by_funcname(hLibintlDLL, "getenv");
668 if (hmsvcrt != NULL)
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100669 {
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100670 dyn_libintl_putenv = (void *)GetProcAddress(hmsvcrt, "_putenv");
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100671 dyn_libintl_wputenv = (void *)GetProcAddress(hmsvcrt, "_wputenv");
672 }
673 if (dyn_libintl_putenv == NULL || dyn_libintl_putenv == _putenv)
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100674 dyn_libintl_putenv = null_libintl_putenv;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100675 if (dyn_libintl_wputenv == NULL || dyn_libintl_wputenv == _wputenv)
676 dyn_libintl_wputenv = null_libintl_wputenv;
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100677
Bram Moolenaar071d4272004-06-13 20:20:40 +0000678 return 1;
679}
680
681 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100682dyn_libintl_end(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000683{
684 if (hLibintlDLL)
685 FreeLibrary(hLibintlDLL);
686 hLibintlDLL = NULL;
687 dyn_libintl_gettext = null_libintl_gettext;
Bram Moolenaaree695f72016-08-03 22:08:45 +0200688 dyn_libintl_ngettext = null_libintl_ngettext;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689 dyn_libintl_textdomain = null_libintl_textdomain;
690 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000691 dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset;
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100692 dyn_libintl_putenv = null_libintl_putenv;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100693 dyn_libintl_wputenv = null_libintl_wputenv;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694}
695
696 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000697null_libintl_gettext(const char *msgid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698{
699 return (char*)msgid;
700}
701
702 static char *
Bram Moolenaaree695f72016-08-03 22:08:45 +0200703null_libintl_ngettext(
704 const char *msgid,
705 const char *msgid_plural,
706 unsigned long n)
707{
Bram Moolenaarc90f2ae2016-08-04 22:00:15 +0200708 return (char *)(n == 1 ? msgid : msgid_plural);
Bram Moolenaaree695f72016-08-03 22:08:45 +0200709}
710
Bram Moolenaaree695f72016-08-03 22:08:45 +0200711 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100712null_libintl_bindtextdomain(
713 const char *domainname UNUSED,
714 const char *dirname UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000715{
716 return NULL;
717}
718
719 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100720null_libintl_bind_textdomain_codeset(
721 const char *domainname UNUSED,
722 const char *codeset UNUSED)
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000723{
724 return NULL;
725}
726
727 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100728null_libintl_textdomain(const char *domainname UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000729{
730 return NULL;
731}
732
Bram Moolenaare0ab9792017-08-02 23:18:25 +0200733 static int
Bram Moolenaar1266d672017-02-01 13:43:36 +0100734null_libintl_putenv(const char *envstring UNUSED)
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100735{
736 return 0;
737}
738
Bram Moolenaare0ab9792017-08-02 23:18:25 +0200739 static int
Bram Moolenaar1266d672017-02-01 13:43:36 +0100740null_libintl_wputenv(const wchar_t *envstring UNUSED)
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100741{
742 return 0;
743}
744
Bram Moolenaar071d4272004-06-13 20:20:40 +0000745#endif /* DYNAMIC_GETTEXT */
746
747/* This symbol is not defined in older versions of the SDK or Visual C++ */
748
749#ifndef VER_PLATFORM_WIN32_WINDOWS
750# define VER_PLATFORM_WIN32_WINDOWS 1
751#endif
752
753DWORD g_PlatformId;
754
755#ifdef HAVE_ACL
Bram Moolenaar82881492012-11-20 16:53:39 +0100756# ifndef PROTO
757# include <aclapi.h>
758# endif
Bram Moolenaar27515922013-06-29 15:36:26 +0200759# ifndef PROTECTED_DACL_SECURITY_INFORMATION
760# define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000L
761# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762#endif
763
Bram Moolenaar27515922013-06-29 15:36:26 +0200764#ifdef HAVE_ACL
765/*
766 * Enables or disables the specified privilege.
767 */
768 static BOOL
769win32_enable_privilege(LPTSTR lpszPrivilege, BOOL bEnable)
770{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100771 BOOL bResult;
772 LUID luid;
773 HANDLE hToken;
774 TOKEN_PRIVILEGES tokenPrivileges;
Bram Moolenaar27515922013-06-29 15:36:26 +0200775
776 if (!OpenProcessToken(GetCurrentProcess(),
777 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
778 return FALSE;
779
780 if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
781 {
782 CloseHandle(hToken);
783 return FALSE;
784 }
785
Bram Moolenaar45500912014-07-09 20:51:07 +0200786 tokenPrivileges.PrivilegeCount = 1;
Bram Moolenaar27515922013-06-29 15:36:26 +0200787 tokenPrivileges.Privileges[0].Luid = luid;
788 tokenPrivileges.Privileges[0].Attributes = bEnable ?
789 SE_PRIVILEGE_ENABLED : 0;
790
791 bResult = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges,
792 sizeof(TOKEN_PRIVILEGES), NULL, NULL);
793
794 CloseHandle(hToken);
795
796 return bResult && GetLastError() == ERROR_SUCCESS;
797}
798#endif
799
Bram Moolenaar071d4272004-06-13 20:20:40 +0000800/*
801 * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
802 * VER_PLATFORM_WIN32_WINDOWS (Win95).
803 */
804 void
805PlatformId(void)
806{
807 static int done = FALSE;
808
809 if (!done)
810 {
811 OSVERSIONINFO ovi;
812
813 ovi.dwOSVersionInfoSize = sizeof(ovi);
814 GetVersionEx(&ovi);
815
816 g_PlatformId = ovi.dwPlatformId;
817
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100818 if ((ovi.dwMajorVersion == 6 && ovi.dwMinorVersion >= 2)
819 || ovi.dwMajorVersion > 6)
820 win8_or_later = TRUE;
821
Bram Moolenaar071d4272004-06-13 20:20:40 +0000822#ifdef HAVE_ACL
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200823 /* Enable privilege for getting or setting SACLs. */
824 win32_enable_privilege(SE_SECURITY_NAME, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000825#endif
826 done = TRUE;
827 }
828}
829
Bram Moolenaar071d4272004-06-13 20:20:40 +0000830#ifndef FEAT_GUI_W32
831
832#define SHIFT (SHIFT_PRESSED)
833#define CTRL (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
834#define ALT (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
835#define ALT_GR (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)
836
837
838/* When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
839 * We map function keys to their ANSI terminal equivalents, as produced
840 * by ANSI.SYS, for compatibility with the MS-DOS version of Vim. Any
841 * ANSI key with a value >= '\300' is nonstandard, but provided anyway
842 * so that the user can have access to all SHIFT-, CTRL-, and ALT-
843 * combinations of function/arrow/etc keys.
844 */
845
Bram Moolenaard6f676d2005-06-01 21:51:55 +0000846static const struct
Bram Moolenaar071d4272004-06-13 20:20:40 +0000847{
848 WORD wVirtKey;
849 BOOL fAnsiKey;
850 int chAlone;
851 int chShift;
852 int chCtrl;
853 int chAlt;
854} VirtKeyMap[] =
855{
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200856// Key ANSI alone shift ctrl alt
Bram Moolenaar071d4272004-06-13 20:20:40 +0000857 { VK_ESCAPE,FALSE, ESC, ESC, ESC, ESC, },
858
859 { VK_F1, TRUE, ';', 'T', '^', 'h', },
860 { VK_F2, TRUE, '<', 'U', '_', 'i', },
861 { VK_F3, TRUE, '=', 'V', '`', 'j', },
862 { VK_F4, TRUE, '>', 'W', 'a', 'k', },
863 { VK_F5, TRUE, '?', 'X', 'b', 'l', },
864 { VK_F6, TRUE, '@', 'Y', 'c', 'm', },
865 { VK_F7, TRUE, 'A', 'Z', 'd', 'n', },
866 { VK_F8, TRUE, 'B', '[', 'e', 'o', },
867 { VK_F9, TRUE, 'C', '\\', 'f', 'p', },
868 { VK_F10, TRUE, 'D', ']', 'g', 'q', },
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200869 { VK_F11, TRUE, '\205', '\207', '\211', '\213', },
870 { VK_F12, TRUE, '\206', '\210', '\212', '\214', },
Bram Moolenaar071d4272004-06-13 20:20:40 +0000871
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200872 { VK_HOME, TRUE, 'G', '\302', 'w', '\303', },
873 { VK_UP, TRUE, 'H', '\304', '\305', '\306', },
874 { VK_PRIOR, TRUE, 'I', '\307', '\204', '\310', }, // PgUp
875 { VK_LEFT, TRUE, 'K', '\311', 's', '\312', },
876 { VK_RIGHT, TRUE, 'M', '\313', 't', '\314', },
877 { VK_END, TRUE, 'O', '\315', 'u', '\316', },
878 { VK_DOWN, TRUE, 'P', '\317', '\320', '\321', },
879 { VK_NEXT, TRUE, 'Q', '\322', 'v', '\323', }, // PgDn
880 { VK_INSERT,TRUE, 'R', '\324', '\325', '\326', },
881 { VK_DELETE,TRUE, 'S', '\327', '\330', '\331', },
Bram Moolenaar071d4272004-06-13 20:20:40 +0000882
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200883 { VK_SNAPSHOT,TRUE, 0, 0, 0, 'r', }, // PrtScrn
Bram Moolenaar071d4272004-06-13 20:20:40 +0000884
885#if 0
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200886 // Most people don't have F13-F20, but what the hell...
887 { VK_F13, TRUE, '\332', '\333', '\334', '\335', },
888 { VK_F14, TRUE, '\336', '\337', '\340', '\341', },
889 { VK_F15, TRUE, '\342', '\343', '\344', '\345', },
890 { VK_F16, TRUE, '\346', '\347', '\350', '\351', },
891 { VK_F17, TRUE, '\352', '\353', '\354', '\355', },
892 { VK_F18, TRUE, '\356', '\357', '\360', '\361', },
893 { VK_F19, TRUE, '\362', '\363', '\364', '\365', },
894 { VK_F20, TRUE, '\366', '\367', '\370', '\371', },
Bram Moolenaar071d4272004-06-13 20:20:40 +0000895#endif
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200896 { VK_ADD, TRUE, 'N', 'N', 'N', 'N', }, // keyp '+'
897 { VK_SUBTRACT, TRUE,'J', 'J', 'J', 'J', }, // keyp '-'
898 // { VK_DIVIDE, TRUE,'N', 'N', 'N', 'N', }, // keyp '/'
899 { VK_MULTIPLY, TRUE,'7', '7', '7', '7', }, // keyp '*'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000900
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200901 { VK_NUMPAD0,TRUE, '\332', '\333', '\334', '\335', },
902 { VK_NUMPAD1,TRUE, '\336', '\337', '\340', '\341', },
903 { VK_NUMPAD2,TRUE, '\342', '\343', '\344', '\345', },
904 { VK_NUMPAD3,TRUE, '\346', '\347', '\350', '\351', },
905 { VK_NUMPAD4,TRUE, '\352', '\353', '\354', '\355', },
906 { VK_NUMPAD5,TRUE, '\356', '\357', '\360', '\361', },
907 { VK_NUMPAD6,TRUE, '\362', '\363', '\364', '\365', },
908 { VK_NUMPAD7,TRUE, '\366', '\367', '\370', '\371', },
909 { VK_NUMPAD8,TRUE, '\372', '\373', '\374', '\375', },
910 // Sorry, out of number space! <negri>
911 { VK_NUMPAD9,TRUE, '\376', '\377', '\377', '\367', },
912
Bram Moolenaar071d4272004-06-13 20:20:40 +0000913};
914
915
916#ifdef _MSC_VER
917// The ToAscii bug destroys several registers. Need to turn off optimization
918// or the GetConsoleKeyboardLayoutName hack will fail in non-debug versions
Bram Moolenaar7b5f8322006-03-23 22:47:08 +0000919# pragma warning(push)
920# pragma warning(disable: 4748)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000921# pragma optimize("", off)
922#endif
923
924#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200925# define UChar UnicodeChar
Bram Moolenaar071d4272004-06-13 20:20:40 +0000926#else
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200927# define UChar uChar.UnicodeChar
Bram Moolenaar071d4272004-06-13 20:20:40 +0000928#endif
929
930/* The return code indicates key code size. */
931 static int
932#ifdef __BORLANDC__
933 __stdcall
934#endif
935win32_kbd_patch_key(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000936 KEY_EVENT_RECORD *pker)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937{
938 UINT uMods = pker->dwControlKeyState;
939 static int s_iIsDead = 0;
940 static WORD awAnsiCode[2];
941 static BYTE abKeystate[256];
942
943
944 if (s_iIsDead == 2)
945 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200946 pker->UChar = (WCHAR) awAnsiCode[1];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000947 s_iIsDead = 0;
948 return 1;
949 }
950
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200951 if (pker->UChar != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000952 return 1;
953
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200954 vim_memset(abKeystate, 0, sizeof (abKeystate));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000955
Bram Moolenaar071d4272004-06-13 20:20:40 +0000956 /* Clear any pending dead keys */
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200957 ToUnicode(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000958
959 if (uMods & SHIFT_PRESSED)
960 abKeystate[VK_SHIFT] = 0x80;
961 if (uMods & CAPSLOCK_ON)
962 abKeystate[VK_CAPITAL] = 1;
963
964 if ((uMods & ALT_GR) == ALT_GR)
965 {
966 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
967 abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
968 }
969
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200970 s_iIsDead = ToUnicode(pker->wVirtualKeyCode, pker->wVirtualScanCode,
971 abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000972
973 if (s_iIsDead > 0)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200974 pker->UChar = (WCHAR) awAnsiCode[0];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000975
976 return s_iIsDead;
977}
978
979#ifdef _MSC_VER
980/* MUST switch optimization on again here, otherwise a call to
981 * decode_key_event() may crash (e.g. when hitting caps-lock) */
982# pragma optimize("", on)
Bram Moolenaar7b5f8322006-03-23 22:47:08 +0000983# pragma warning(pop)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000984
985# if (_MSC_VER < 1100)
986/* MUST turn off global optimisation for this next function, or
987 * pressing ctrl-minus in insert mode crashes Vim when built with
988 * VC4.1. -- negri. */
989# pragma optimize("g", off)
990# endif
991#endif
992
993static BOOL g_fJustGotFocus = FALSE;
994
995/*
996 * Decode a KEY_EVENT into one or two keystrokes
997 */
998 static BOOL
999decode_key_event(
1000 KEY_EVENT_RECORD *pker,
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001001 WCHAR *pch,
1002 WCHAR *pch2,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001003 int *pmodifiers,
1004 BOOL fDoPost)
1005{
1006 int i;
1007 const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
1008
1009 *pch = *pch2 = NUL;
1010 g_fJustGotFocus = FALSE;
1011
1012 /* ignore key up events */
1013 if (!pker->bKeyDown)
1014 return FALSE;
1015
1016 /* ignore some keystrokes */
1017 switch (pker->wVirtualKeyCode)
1018 {
1019 /* modifiers */
1020 case VK_SHIFT:
1021 case VK_CONTROL:
1022 case VK_MENU: /* Alt key */
1023 return FALSE;
1024
1025 default:
1026 break;
1027 }
1028
1029 /* special cases */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001030 if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->UChar == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001031 {
1032 /* Ctrl-6 is Ctrl-^ */
1033 if (pker->wVirtualKeyCode == '6')
1034 {
1035 *pch = Ctrl_HAT;
1036 return TRUE;
1037 }
1038 /* Ctrl-2 is Ctrl-@ */
1039 else if (pker->wVirtualKeyCode == '2')
1040 {
1041 *pch = NUL;
1042 return TRUE;
1043 }
1044 /* Ctrl-- is Ctrl-_ */
1045 else if (pker->wVirtualKeyCode == 0xBD)
1046 {
1047 *pch = Ctrl__;
1048 return TRUE;
1049 }
1050 }
1051
1052 /* Shift-TAB */
1053 if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED))
1054 {
1055 *pch = K_NUL;
1056 *pch2 = '\017';
1057 return TRUE;
1058 }
1059
1060 for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]); --i >= 0; )
1061 {
1062 if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
1063 {
1064 if (nModifs == 0)
1065 *pch = VirtKeyMap[i].chAlone;
1066 else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
1067 *pch = VirtKeyMap[i].chShift;
1068 else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
1069 *pch = VirtKeyMap[i].chCtrl;
1070 else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
1071 *pch = VirtKeyMap[i].chAlt;
1072
1073 if (*pch != 0)
1074 {
1075 if (VirtKeyMap[i].fAnsiKey)
1076 {
1077 *pch2 = *pch;
1078 *pch = K_NUL;
1079 }
1080
1081 return TRUE;
1082 }
1083 }
1084 }
1085
1086 i = win32_kbd_patch_key(pker);
1087
1088 if (i < 0)
1089 *pch = NUL;
1090 else
1091 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001092 *pch = (i > 0) ? pker->UChar : NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001093
1094 if (pmodifiers != NULL)
1095 {
1096 /* Pass on the ALT key as a modifier, but only when not combined
1097 * with CTRL (which is ALTGR). */
1098 if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0)
1099 *pmodifiers |= MOD_MASK_ALT;
1100
1101 /* Pass on SHIFT only for special keys, because we don't know when
1102 * it's already included with the character. */
1103 if ((nModifs & SHIFT) != 0 && *pch <= 0x20)
1104 *pmodifiers |= MOD_MASK_SHIFT;
1105
1106 /* Pass on CTRL only for non-special keys, because we don't know
1107 * when it's already included with the character. And not when
1108 * combined with ALT (which is ALTGR). */
1109 if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0
1110 && *pch >= 0x20 && *pch < 0x80)
1111 *pmodifiers |= MOD_MASK_CTRL;
1112 }
1113 }
1114
1115 return (*pch != NUL);
1116}
1117
1118#ifdef _MSC_VER
1119# pragma optimize("", on)
1120#endif
1121
1122#endif /* FEAT_GUI_W32 */
1123
1124
1125#ifdef FEAT_MOUSE
1126
1127/*
1128 * For the GUI the mouse handling is in gui_w32.c.
1129 */
1130# ifdef FEAT_GUI_W32
1131 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01001132mch_setmouse(int on UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001133{
1134}
1135# else
1136static int g_fMouseAvail = FALSE; /* mouse present */
1137static int g_fMouseActive = FALSE; /* mouse enabled */
1138static int g_nMouseClick = -1; /* mouse status */
1139static int g_xMouse; /* mouse x coordinate */
1140static int g_yMouse; /* mouse y coordinate */
1141
1142/*
1143 * Enable or disable mouse input
1144 */
1145 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001146mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001147{
1148 DWORD cmodein;
1149
1150 if (!g_fMouseAvail)
1151 return;
1152
1153 g_fMouseActive = on;
1154 GetConsoleMode(g_hConIn, &cmodein);
1155
1156 if (g_fMouseActive)
1157 cmodein |= ENABLE_MOUSE_INPUT;
1158 else
1159 cmodein &= ~ENABLE_MOUSE_INPUT;
1160
1161 SetConsoleMode(g_hConIn, cmodein);
1162}
1163
Bram Moolenaar157d8132018-03-06 17:09:20 +01001164
1165#if defined(FEAT_BEVAL_TERM) || defined(PROTO)
1166/*
1167 * Called when 'balloonevalterm' changed.
1168 */
1169 void
1170mch_bevalterm_changed(void)
1171{
1172 mch_setmouse(g_fMouseActive);
1173}
1174#endif
1175
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176/*
1177 * Decode a MOUSE_EVENT. If it's a valid event, return MOUSE_LEFT,
1178 * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
1179 * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
1180 * or a MOUSE_LEFT, _MIDDLE, or _RIGHT. We encode the button type,
1181 * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
1182 * and we return the mouse position in g_xMouse and g_yMouse.
1183 *
1184 * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
1185 * MOUSE_DRAGs and one MOUSE_RELEASE. MOUSE_RELEASE will be followed only
1186 * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
1187 *
1188 * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
1189 * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
1190 *
1191 * Windows will send us MOUSE_MOVED notifications whenever the mouse
1192 * moves, even if it stays within the same character cell. We ignore
1193 * all MOUSE_MOVED messages if the position hasn't really changed, and
1194 * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
1195 * we're only interested in MOUSE_DRAG).
1196 *
1197 * All of this is complicated by the code that fakes MOUSE_MIDDLE on
1198 * 2-button mouses by pressing the left & right buttons simultaneously.
1199 * In practice, it's almost impossible to click both at the same time,
1200 * so we need to delay a little. Also, we tend not to get MOUSE_RELEASE
1201 * in such cases, if the user is clicking quickly.
1202 */
1203 static BOOL
1204decode_mouse_event(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001205 MOUSE_EVENT_RECORD *pmer)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001206{
1207 static int s_nOldButton = -1;
1208 static int s_nOldMouseClick = -1;
1209 static int s_xOldMouse = -1;
1210 static int s_yOldMouse = -1;
1211 static linenr_T s_old_topline = 0;
1212#ifdef FEAT_DIFF
1213 static int s_old_topfill = 0;
1214#endif
1215 static int s_cClicks = 1;
1216 static BOOL s_fReleased = TRUE;
1217 static DWORD s_dwLastClickTime = 0;
1218 static BOOL s_fNextIsMiddle = FALSE;
1219
1220 static DWORD cButtons = 0; /* number of buttons supported */
1221
1222 const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
1223 const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
1224 const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
1225 const DWORD LEFT_RIGHT = LEFT | RIGHT;
1226
1227 int nButton;
1228
1229 if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons))
1230 cButtons = 2;
1231
1232 if (!g_fMouseAvail || !g_fMouseActive)
1233 {
1234 g_nMouseClick = -1;
1235 return FALSE;
1236 }
1237
1238 /* get a spurious MOUSE_EVENT immediately after receiving focus; ignore */
1239 if (g_fJustGotFocus)
1240 {
1241 g_fJustGotFocus = FALSE;
1242 return FALSE;
1243 }
1244
1245 /* unprocessed mouse click? */
1246 if (g_nMouseClick != -1)
1247 return TRUE;
1248
1249 nButton = -1;
1250 g_xMouse = pmer->dwMousePosition.X;
1251 g_yMouse = pmer->dwMousePosition.Y;
1252
1253 if (pmer->dwEventFlags == MOUSE_MOVED)
1254 {
Bram Moolenaar157d8132018-03-06 17:09:20 +01001255 /* Ignore MOUSE_MOVED events if (x, y) hasn't changed. (We get these
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256 * events even when the mouse moves only within a char cell.) */
1257 if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse)
1258 return FALSE;
1259 }
1260
1261 /* If no buttons are pressed... */
1262 if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0)
1263 {
Bram Moolenaar157d8132018-03-06 17:09:20 +01001264 nButton = MOUSE_RELEASE;
1265
Bram Moolenaar071d4272004-06-13 20:20:40 +00001266 /* If the last thing returned was MOUSE_RELEASE, ignore this */
1267 if (s_fReleased)
Bram Moolenaar157d8132018-03-06 17:09:20 +01001268 {
1269#ifdef FEAT_BEVAL_TERM
1270 /* do return mouse move events when we want them */
1271 if (p_bevalterm)
1272 nButton = MOUSE_DRAG;
1273 else
1274#endif
1275 return FALSE;
1276 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001277
Bram Moolenaar071d4272004-06-13 20:20:40 +00001278 s_fReleased = TRUE;
1279 }
1280 else /* one or more buttons pressed */
1281 {
1282 /* on a 2-button mouse, hold down left and right buttons
1283 * simultaneously to get MIDDLE. */
1284
1285 if (cButtons == 2 && s_nOldButton != MOUSE_DRAG)
1286 {
1287 DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
1288
1289 /* if either left or right button only is pressed, see if the
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02001290 * next mouse event has both of them pressed */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001291 if (dwLR == LEFT || dwLR == RIGHT)
1292 {
1293 for (;;)
1294 {
1295 /* wait a short time for next input event */
1296 if (WaitForSingleObject(g_hConIn, p_mouset / 3)
1297 != WAIT_OBJECT_0)
1298 break;
1299 else
1300 {
1301 DWORD cRecords = 0;
1302 INPUT_RECORD ir;
1303 MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
1304
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001305 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306
1307 if (cRecords == 0 || ir.EventType != MOUSE_EVENT
1308 || !(pmer2->dwButtonState & LEFT_RIGHT))
1309 break;
1310 else
1311 {
1312 if (pmer2->dwEventFlags != MOUSE_MOVED)
1313 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001314 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001315
1316 return decode_mouse_event(pmer2);
1317 }
1318 else if (s_xOldMouse == pmer2->dwMousePosition.X &&
1319 s_yOldMouse == pmer2->dwMousePosition.Y)
1320 {
1321 /* throw away spurious mouse move */
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001322 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323
1324 /* are there any more mouse events in queue? */
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001325 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001326
1327 if (cRecords==0 || ir.EventType != MOUSE_EVENT)
1328 break;
1329 }
1330 else
1331 break;
1332 }
1333 }
1334 }
1335 }
1336 }
1337
1338 if (s_fNextIsMiddle)
1339 {
1340 nButton = (pmer->dwEventFlags == MOUSE_MOVED)
1341 ? MOUSE_DRAG : MOUSE_MIDDLE;
1342 s_fNextIsMiddle = FALSE;
1343 }
1344 else if (cButtons == 2 &&
1345 ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
1346 {
1347 nButton = MOUSE_MIDDLE;
1348
1349 if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED)
1350 {
1351 s_fNextIsMiddle = TRUE;
1352 nButton = MOUSE_RELEASE;
1353 }
1354 }
1355 else if ((pmer->dwButtonState & LEFT) == LEFT)
1356 nButton = MOUSE_LEFT;
1357 else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
1358 nButton = MOUSE_MIDDLE;
1359 else if ((pmer->dwButtonState & RIGHT) == RIGHT)
1360 nButton = MOUSE_RIGHT;
1361
1362 if (! s_fReleased && ! s_fNextIsMiddle
1363 && nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG)
1364 return FALSE;
1365
1366 s_fReleased = s_fNextIsMiddle;
1367 }
1368
1369 if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK)
1370 {
1371 /* button pressed or released, without mouse moving */
1372 if (nButton != -1 && nButton != MOUSE_RELEASE)
1373 {
1374 DWORD dwCurrentTime = GetTickCount();
1375
1376 if (s_xOldMouse != g_xMouse
1377 || s_yOldMouse != g_yMouse
1378 || s_nOldButton != nButton
1379 || s_old_topline != curwin->w_topline
1380#ifdef FEAT_DIFF
1381 || s_old_topfill != curwin->w_topfill
1382#endif
1383 || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset)
1384 {
1385 s_cClicks = 1;
1386 }
1387 else if (++s_cClicks > 4)
1388 {
1389 s_cClicks = 1;
1390 }
1391
1392 s_dwLastClickTime = dwCurrentTime;
1393 }
1394 }
1395 else if (pmer->dwEventFlags == MOUSE_MOVED)
1396 {
1397 if (nButton != -1 && nButton != MOUSE_RELEASE)
1398 nButton = MOUSE_DRAG;
1399
1400 s_cClicks = 1;
1401 }
1402
1403 if (nButton == -1)
1404 return FALSE;
1405
1406 if (nButton != MOUSE_RELEASE)
1407 s_nOldButton = nButton;
1408
1409 g_nMouseClick = nButton;
1410
1411 if (pmer->dwControlKeyState & SHIFT_PRESSED)
1412 g_nMouseClick |= MOUSE_SHIFT;
1413 if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
1414 g_nMouseClick |= MOUSE_CTRL;
1415 if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
1416 g_nMouseClick |= MOUSE_ALT;
1417
1418 if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE)
1419 SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
1420
1421 /* only pass on interesting (i.e., different) mouse events */
1422 if (s_xOldMouse == g_xMouse
1423 && s_yOldMouse == g_yMouse
1424 && s_nOldMouseClick == g_nMouseClick)
1425 {
1426 g_nMouseClick = -1;
1427 return FALSE;
1428 }
1429
1430 s_xOldMouse = g_xMouse;
1431 s_yOldMouse = g_yMouse;
1432 s_old_topline = curwin->w_topline;
1433#ifdef FEAT_DIFF
1434 s_old_topfill = curwin->w_topfill;
1435#endif
1436 s_nOldMouseClick = g_nMouseClick;
1437
1438 return TRUE;
1439}
1440
1441# endif /* FEAT_GUI_W32 */
1442#endif /* FEAT_MOUSE */
1443
1444
1445#ifdef MCH_CURSOR_SHAPE
1446/*
1447 * Set the shape of the cursor.
1448 * 'thickness' can be from 1 (thin) to 99 (block)
1449 */
1450 static void
1451mch_set_cursor_shape(int thickness)
1452{
1453 CONSOLE_CURSOR_INFO ConsoleCursorInfo;
1454 ConsoleCursorInfo.dwSize = thickness;
1455 ConsoleCursorInfo.bVisible = s_cursor_visible;
1456
1457 SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo);
1458 if (s_cursor_visible)
1459 SetConsoleCursorPosition(g_hConOut, g_coord);
1460}
1461
1462 void
1463mch_update_cursor(void)
1464{
1465 int idx;
1466 int thickness;
1467
1468 /*
1469 * How the cursor is drawn depends on the current mode.
1470 */
1471 idx = get_shape_idx(FALSE);
1472
1473 if (shape_table[idx].shape == SHAPE_BLOCK)
1474 thickness = 99; /* 100 doesn't work on W95 */
1475 else
1476 thickness = shape_table[idx].percentage;
1477 mch_set_cursor_shape(thickness);
1478}
1479#endif
1480
1481#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1482/*
1483 * Handle FOCUS_EVENT.
1484 */
1485 static void
1486handle_focus_event(INPUT_RECORD ir)
1487{
1488 g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
1489 ui_focus_change((int)g_fJustGotFocus);
1490}
1491
1492/*
1493 * Wait until console input from keyboard or mouse is available,
1494 * or the time is up.
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001495 * When "ignore_input" is TRUE even wait when input is available.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001496 * Return TRUE if something is available FALSE if not.
1497 */
1498 static int
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001499WaitForChar(long msec, int ignore_input)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500{
1501 DWORD dwNow = 0, dwEndTime = 0;
1502 INPUT_RECORD ir;
1503 DWORD cRecords;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001504 WCHAR ch, ch2;
Bram Moolenaar4445f7e2016-04-20 20:55:56 +02001505#ifdef FEAT_TIMERS
Bram Moolenaar40b1b542016-04-20 20:18:23 +02001506 int tb_change_cnt = typebuf.tb_change_cnt;
Bram Moolenaar4445f7e2016-04-20 20:55:56 +02001507#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508
1509 if (msec > 0)
1510 /* Wait until the specified time has elapsed. */
1511 dwEndTime = GetTickCount() + msec;
1512 else if (msec < 0)
1513 /* Wait forever. */
1514 dwEndTime = INFINITE;
1515
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01001516 // We need to loop until the end of the time period, because
1517 // we might get multiple unusable mouse events in that time.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001518 for (;;)
1519 {
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001520 // Only process messages when waiting.
1521 if (msec != 0)
1522 {
Bram Moolenaarca568ae2016-02-01 21:32:58 +01001523#ifdef MESSAGE_QUEUE
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001524 parse_queued_messages();
Bram Moolenaarca568ae2016-02-01 21:32:58 +01001525#endif
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001526#ifdef FEAT_MZSCHEME
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001527 mzvim_check_threads();
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001528#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001529#ifdef FEAT_CLIENTSERVER
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001530 serverProcessPendingMessages();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001531#endif
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001532 }
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001533
Bram Moolenaar071d4272004-06-13 20:20:40 +00001534 if (0
1535#ifdef FEAT_MOUSE
1536 || g_nMouseClick != -1
1537#endif
1538#ifdef FEAT_CLIENTSERVER
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001539 || (!ignore_input && input_available())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001540#endif
1541 )
1542 return TRUE;
1543
1544 if (msec > 0)
1545 {
Bram Moolenaarb7512b72013-08-10 12:45:09 +02001546 /* If the specified wait time has passed, return. Beware that
1547 * GetTickCount() may wrap around (overflow). */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001548 dwNow = GetTickCount();
Bram Moolenaarb7512b72013-08-10 12:45:09 +02001549 if ((int)(dwNow - dwEndTime) >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001550 break;
1551 }
1552 if (msec != 0)
1553 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001554 DWORD dwWaitTime = dwEndTime - dwNow;
1555
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01001556#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar8a8199e2016-11-26 15:13:33 +01001557 /* Check channel while waiting for input. */
Bram Moolenaar9186a272016-02-23 19:34:01 +01001558 if (dwWaitTime > 100)
Bram Moolenaar8a8199e2016-11-26 15:13:33 +01001559 {
Bram Moolenaar9186a272016-02-23 19:34:01 +01001560 dwWaitTime = 100;
Bram Moolenaar8a8199e2016-11-26 15:13:33 +01001561 /* If there is readahead then parse_queued_messages() timed out
1562 * and we should call it again soon. */
1563 if (channel_any_readahead())
1564 dwWaitTime = 10;
1565 }
Bram Moolenaar9186a272016-02-23 19:34:01 +01001566#endif
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01001567#ifdef FEAT_BEVAL_GUI
Bram Moolenaar59716a22017-03-01 20:32:44 +01001568 if (p_beval && dwWaitTime > 100)
1569 /* The 'balloonexpr' may indirectly invoke a callback while
1570 * waiting for a character, need to check often. */
1571 dwWaitTime = 100;
1572#endif
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001573#ifdef FEAT_MZSCHEME
1574 if (mzthreads_allowed() && p_mzq > 0
1575 && (msec < 0 || (long)dwWaitTime > p_mzq))
1576 dwWaitTime = p_mzq; /* don't wait longer than 'mzquantum' */
1577#endif
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001578#ifdef FEAT_TIMERS
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001579 // When waiting very briefly don't trigger timers.
1580 if (dwWaitTime > 10)
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001581 {
1582 long due_time;
1583
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001584 // Trigger timers and then get the time in msec until the next
1585 // one is due. Wait up to that time.
1586 due_time = check_due_timer();
1587 if (typebuf.tb_change_cnt != tb_change_cnt)
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001588 {
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001589 // timer may have used feedkeys().
1590 return FALSE;
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001591 }
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001592 if (due_time > 0 && dwWaitTime > (DWORD)due_time)
1593 dwWaitTime = due_time;
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001594 }
1595#endif
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001596 if (
Bram Moolenaar071d4272004-06-13 20:20:40 +00001597#ifdef FEAT_CLIENTSERVER
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001598 // Wait for either an event on the console input or a
1599 // message in the client-server window.
1600 msg_wait_for_multiple_objects(1, &g_hConIn, FALSE,
1601 dwWaitTime, QS_SENDMESSAGE) != WAIT_OBJECT_0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001602#else
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001603 wait_for_single_object(g_hConIn, dwWaitTime)
1604 != WAIT_OBJECT_0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001605#endif
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001606 )
1607 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001608 }
1609
1610 cRecords = 0;
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001611 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001612
1613#ifdef FEAT_MBYTE_IME
1614 if (State & CMDLINE && msg_row == Rows - 1)
1615 {
1616 CONSOLE_SCREEN_BUFFER_INFO csbi;
1617
1618 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1619 {
1620 if (csbi.dwCursorPosition.Y != msg_row)
1621 {
1622 /* The screen is now messed up, must redraw the
1623 * command line and later all the windows. */
1624 redraw_all_later(CLEAR);
1625 cmdline_row -= (msg_row - csbi.dwCursorPosition.Y);
1626 redrawcmd();
1627 }
1628 }
1629 }
1630#endif
1631
1632 if (cRecords > 0)
1633 {
1634 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
1635 {
1636#ifdef FEAT_MBYTE_IME
1637 /* Windows IME sends two '\n's with only one 'ENTER'. First:
1638 * wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001639 if (ir.Event.KeyEvent.UChar == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001640 && ir.Event.KeyEvent.wVirtualKeyCode == 13)
1641 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001642 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001643 continue;
1644 }
1645#endif
1646 if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
1647 NULL, FALSE))
1648 return TRUE;
1649 }
1650
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001651 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001652
1653 if (ir.EventType == FOCUS_EVENT)
1654 handle_focus_event(ir);
1655 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
Bram Moolenaarc33ecb22018-02-11 16:40:45 +01001656 {
1657 /* Only call shell_resized() when the size actually change to
1658 * avoid the screen is cleard. */
1659 if (ir.Event.WindowBufferSizeEvent.dwSize.X != Columns
1660 || ir.Event.WindowBufferSizeEvent.dwSize.Y != Rows)
1661 shell_resized();
1662 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001663#ifdef FEAT_MOUSE
1664 else if (ir.EventType == MOUSE_EVENT
1665 && decode_mouse_event(&ir.Event.MouseEvent))
1666 return TRUE;
1667#endif
1668 }
1669 else if (msec == 0)
1670 break;
1671 }
1672
1673#ifdef FEAT_CLIENTSERVER
1674 /* Something might have been received while we were waiting. */
1675 if (input_available())
1676 return TRUE;
1677#endif
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001678
Bram Moolenaar071d4272004-06-13 20:20:40 +00001679 return FALSE;
1680}
1681
1682#ifndef FEAT_GUI_MSWIN
1683/*
1684 * return non-zero if a character is available
1685 */
1686 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001687mch_char_avail(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001688{
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001689 return WaitForChar(0L, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001690}
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001691
1692# if defined(FEAT_TERMINAL) || defined(PROTO)
1693/*
1694 * Check for any pending input or messages.
1695 */
1696 int
1697mch_check_messages(void)
1698{
1699 return WaitForChar(0L, TRUE);
1700}
1701# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001702#endif
1703
1704/*
1705 * Create the console input. Used when reading stdin doesn't work.
1706 */
1707 static void
1708create_conin(void)
1709{
1710 g_hConIn = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
1711 FILE_SHARE_READ|FILE_SHARE_WRITE,
1712 (LPSECURITY_ATTRIBUTES) NULL,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001713 OPEN_EXISTING, 0, (HANDLE)NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001714 did_create_conin = TRUE;
1715}
1716
1717/*
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001718 * Get a keystroke or a mouse event, use a blocking wait.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001719 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001720 static WCHAR
1721tgetch(int *pmodifiers, WCHAR *pch2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001722{
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001723 WCHAR ch;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001724
1725 for (;;)
1726 {
1727 INPUT_RECORD ir;
1728 DWORD cRecords = 0;
1729
1730#ifdef FEAT_CLIENTSERVER
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001731 (void)WaitForChar(-1L, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001732 if (input_available())
1733 return 0;
1734# ifdef FEAT_MOUSE
1735 if (g_nMouseClick != -1)
1736 return 0;
1737# endif
1738#endif
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001739 if (read_console_input(g_hConIn, &ir, 1, &cRecords) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001740 {
1741 if (did_create_conin)
1742 read_error_exit();
1743 create_conin();
1744 continue;
1745 }
1746
1747 if (ir.EventType == KEY_EVENT)
1748 {
1749 if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2,
1750 pmodifiers, TRUE))
1751 return ch;
1752 }
1753 else if (ir.EventType == FOCUS_EVENT)
1754 handle_focus_event(ir);
1755 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1756 shell_resized();
1757#ifdef FEAT_MOUSE
1758 else if (ir.EventType == MOUSE_EVENT)
1759 {
1760 if (decode_mouse_event(&ir.Event.MouseEvent))
1761 return 0;
1762 }
1763#endif
1764 }
1765}
1766#endif /* !FEAT_GUI_W32 */
1767
1768
1769/*
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02001770 * mch_inchar(): low-level input function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001771 * Get one or more characters from the keyboard or the mouse.
1772 * If time == 0, do not wait for characters.
1773 * If time == n, wait a short time for characters.
1774 * If time == -1, wait forever for characters.
1775 * Returns the number of characters read into buf.
1776 */
1777 int
1778mch_inchar(
Bram Moolenaar1266d672017-02-01 13:43:36 +01001779 char_u *buf UNUSED,
1780 int maxlen UNUSED,
1781 long time UNUSED,
1782 int tb_change_cnt UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001783{
1784#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1785
1786 int len;
1787 int c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788#define TYPEAHEADLEN 20
1789 static char_u typeahead[TYPEAHEADLEN]; /* previously typed bytes. */
1790 static int typeaheadlen = 0;
1791
1792 /* First use any typeahead that was kept because "buf" was too small. */
1793 if (typeaheadlen > 0)
1794 goto theend;
1795
Bram Moolenaar071d4272004-06-13 20:20:40 +00001796 if (time >= 0)
1797 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001798 if (!WaitForChar(time, FALSE)) /* no character available */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800 }
1801 else /* time == -1, wait forever */
1802 {
1803 mch_set_winsize_now(); /* Allow winsize changes from now on */
1804
Bram Moolenaar3918c952005-03-15 22:34:55 +00001805 /*
1806 * If there is no character available within 2 seconds (default)
1807 * write the autoscript file to disk. Or cause the CursorHold event
1808 * to be triggered.
1809 */
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001810 if (!WaitForChar(p_ut, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 {
Bram Moolenaard35f9712005-12-18 22:02:33 +00001812 if (trigger_cursorhold() && maxlen >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001813 {
Bram Moolenaar3918c952005-03-15 22:34:55 +00001814 buf[0] = K_SPECIAL;
1815 buf[1] = KS_EXTRA;
1816 buf[2] = (int)KE_CURSORHOLD;
1817 return 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001818 }
Bram Moolenaar702517d2005-06-27 22:34:07 +00001819 before_blocking();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001820 }
1821 }
1822
1823 /*
1824 * Try to read as many characters as there are, until the buffer is full.
1825 */
1826
1827 /* we will get at least one key. Get more if they are available. */
1828 g_fCBrkPressed = FALSE;
1829
1830#ifdef MCH_WRITE_DUMP
1831 if (fdDump)
1832 fputc('[', fdDump);
1833#endif
1834
1835 /* Keep looping until there is something in the typeahead buffer and more
1836 * to get and still room in the buffer (up to two bytes for a char and
1837 * three bytes for a modifier). */
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001838 while ((typeaheadlen == 0 || WaitForChar(0L, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001839 && typeaheadlen + 5 <= TYPEAHEADLEN)
1840 {
1841 if (typebuf_changed(tb_change_cnt))
1842 {
1843 /* "buf" may be invalid now if a client put something in the
1844 * typeahead buffer and "buf" is in the typeahead buffer. */
1845 typeaheadlen = 0;
1846 break;
1847 }
1848#ifdef FEAT_MOUSE
1849 if (g_nMouseClick != -1)
1850 {
1851# ifdef MCH_WRITE_DUMP
1852 if (fdDump)
1853 fprintf(fdDump, "{%02x @ %d, %d}",
1854 g_nMouseClick, g_xMouse, g_yMouse);
1855# endif
1856 typeahead[typeaheadlen++] = ESC + 128;
1857 typeahead[typeaheadlen++] = 'M';
1858 typeahead[typeaheadlen++] = g_nMouseClick;
1859 typeahead[typeaheadlen++] = g_xMouse + '!';
1860 typeahead[typeaheadlen++] = g_yMouse + '!';
1861 g_nMouseClick = -1;
1862 }
1863 else
1864#endif
1865 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001866 WCHAR ch2 = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001867 int modifiers = 0;
1868
1869 c = tgetch(&modifiers, &ch2);
1870
1871 if (typebuf_changed(tb_change_cnt))
1872 {
1873 /* "buf" may be invalid now if a client put something in the
1874 * typeahead buffer and "buf" is in the typeahead buffer. */
1875 typeaheadlen = 0;
1876 break;
1877 }
1878
1879 if (c == Ctrl_C && ctrl_c_interrupts)
1880 {
1881#if defined(FEAT_CLIENTSERVER)
1882 trash_input_buf();
1883#endif
1884 got_int = TRUE;
1885 }
1886
1887#ifdef FEAT_MOUSE
1888 if (g_nMouseClick == -1)
1889#endif
1890 {
1891 int n = 1;
1892
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001893 if (ch2 == NUL)
1894 {
1895 int i;
1896 char_u *p;
1897 WCHAR ch[2];
1898
1899 ch[0] = c;
1900 if (c >= 0xD800 && c <= 0xDBFF) /* High surrogate */
1901 {
1902 ch[1] = tgetch(&modifiers, &ch2);
1903 n++;
1904 }
1905 p = utf16_to_enc(ch, &n);
1906 if (p != NULL)
1907 {
1908 for (i = 0; i < n; i++)
1909 typeahead[typeaheadlen + i] = p[i];
1910 vim_free(p);
1911 }
1912 }
1913 else
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001914 typeahead[typeaheadlen] = c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001915 if (ch2 != NUL)
1916 {
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +02001917 if (c == K_NUL)
Bram Moolenaarfeeb4d02017-12-05 15:14:46 +01001918 {
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +02001919 switch (ch2)
1920 {
1921 case (WCHAR)'\324': // SHIFT+Insert
1922 case (WCHAR)'\325': // CTRL+Insert
1923 case (WCHAR)'\327': // SHIFT+Delete
1924 case (WCHAR)'\330': // CTRL+Delete
1925 typeahead[typeaheadlen + n] = (char_u)ch2;
1926 n++;
1927 break;
1928
1929 default:
1930 typeahead[typeaheadlen + n] = 3;
1931 typeahead[typeaheadlen + n + 1] = (char_u)ch2;
1932 n += 2;
1933 break;
1934 }
Bram Moolenaarfeeb4d02017-12-05 15:14:46 +01001935 }
1936 else
1937 {
1938 typeahead[typeaheadlen + n] = 3;
1939 typeahead[typeaheadlen + n + 1] = (char_u)ch2;
1940 n += 2;
1941 }
Bram Moolenaar45500912014-07-09 20:51:07 +02001942 }
1943
Bram Moolenaar071d4272004-06-13 20:20:40 +00001944 /* Use the ALT key to set the 8th bit of the character
1945 * when it's one byte, the 8th bit isn't set yet and not
1946 * using a double-byte encoding (would become a lead
1947 * byte). */
1948 if ((modifiers & MOD_MASK_ALT)
1949 && n == 1
1950 && (typeahead[typeaheadlen] & 0x80) == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001951 && !enc_dbcs
Bram Moolenaar071d4272004-06-13 20:20:40 +00001952 )
1953 {
Bram Moolenaar85a3e5c2007-11-20 16:22:16 +00001954 n = (*mb_char2bytes)(typeahead[typeaheadlen] | 0x80,
1955 typeahead + typeaheadlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001956 modifiers &= ~MOD_MASK_ALT;
1957 }
1958
1959 if (modifiers != 0)
1960 {
1961 /* Prepend modifiers to the character. */
1962 mch_memmove(typeahead + typeaheadlen + 3,
1963 typeahead + typeaheadlen, n);
1964 typeahead[typeaheadlen++] = K_SPECIAL;
1965 typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
1966 typeahead[typeaheadlen++] = modifiers;
1967 }
1968
1969 typeaheadlen += n;
1970
1971#ifdef MCH_WRITE_DUMP
1972 if (fdDump)
1973 fputc(c, fdDump);
1974#endif
1975 }
1976 }
1977 }
1978
1979#ifdef MCH_WRITE_DUMP
1980 if (fdDump)
1981 {
1982 fputs("]\n", fdDump);
1983 fflush(fdDump);
1984 }
1985#endif
1986
Bram Moolenaar071d4272004-06-13 20:20:40 +00001987theend:
1988 /* Move typeahead to "buf", as much as fits. */
1989 len = 0;
1990 while (len < maxlen && typeaheadlen > 0)
1991 {
1992 buf[len++] = typeahead[0];
1993 mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
1994 }
1995 return len;
1996
1997#else /* FEAT_GUI_W32 */
1998 return 0;
1999#endif /* FEAT_GUI_W32 */
2000}
2001
Bram Moolenaar82881492012-11-20 16:53:39 +01002002#ifndef PROTO
2003# ifndef __MINGW32__
2004# include <shellapi.h> /* required for FindExecutable() */
2005# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002006#endif
2007
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002008/*
Bram Moolenaar43663192017-03-05 14:29:12 +01002009 * If "use_path" is TRUE: Return TRUE if "name" is in $PATH.
2010 * If "use_path" is FALSE: Return TRUE if "name" exists.
2011 * When returning TRUE and "path" is not NULL save the path and set "*path" to
2012 * the allocated memory.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00002013 * TODO: Should somehow check if it's really executable.
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002014 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002015 static int
Bram Moolenaar43663192017-03-05 14:29:12 +01002016executable_exists(char *name, char_u **path, int use_path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002017{
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002018 char *dum;
2019 char fname[_MAX_PATH];
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02002020 char *curpath, *newpath;
2021 long n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002022
Bram Moolenaar43663192017-03-05 14:29:12 +01002023 if (!use_path)
2024 {
Bram Moolenaarf7e894d2017-03-05 19:49:13 +01002025 if (mch_getperm((char_u *)name) != -1 && !mch_isdir((char_u *)name))
Bram Moolenaar43663192017-03-05 14:29:12 +01002026 {
2027 if (path != NULL)
Bram Moolenaar066029e2017-03-05 15:19:32 +01002028 {
Bram Moolenaarf7e894d2017-03-05 19:49:13 +01002029 if (mch_isFullName((char_u *)name))
Bram Moolenaar066029e2017-03-05 15:19:32 +01002030 *path = vim_strsave((char_u *)name);
2031 else
2032 *path = FullName_save((char_u *)name, FALSE);
2033 }
Bram Moolenaar43663192017-03-05 14:29:12 +01002034 return TRUE;
2035 }
2036 return FALSE;
2037 }
2038
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002039 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002040 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002041 WCHAR *p = enc_to_utf16((char_u *)name, NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002042 WCHAR fnamew[_MAX_PATH];
2043 WCHAR *dumw;
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02002044 WCHAR *wcurpath, *wnewpath;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002045
2046 if (p != NULL)
2047 {
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02002048 wcurpath = _wgetenv(L"PATH");
2049 wnewpath = (WCHAR*)alloc((unsigned)(wcslen(wcurpath) + 3)
2050 * sizeof(WCHAR));
2051 if (wnewpath == NULL)
2052 return FALSE;
2053 wcscpy(wnewpath, L".;");
2054 wcscat(wnewpath, wcurpath);
2055 n = (long)SearchPathW(wnewpath, p, NULL, _MAX_PATH, fnamew, &dumw);
2056 vim_free(wnewpath);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002057 vim_free(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002058 if (n == 0)
2059 return FALSE;
2060 if (GetFileAttributesW(fnamew) & FILE_ATTRIBUTE_DIRECTORY)
2061 return FALSE;
2062 if (path != NULL)
2063 *path = utf16_to_enc(fnamew, NULL);
2064 return TRUE;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002065 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002066 }
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02002067
2068 curpath = getenv("PATH");
2069 newpath = (char*)alloc((unsigned)(STRLEN(curpath) + 3));
2070 if (newpath == NULL)
2071 return FALSE;
2072 STRCPY(newpath, ".;");
2073 STRCAT(newpath, curpath);
2074 n = (long)SearchPath(newpath, name, NULL, _MAX_PATH, fname, &dum);
2075 vim_free(newpath);
2076 if (n == 0)
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002077 return FALSE;
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002078 if (mch_isdir((char_u *)fname))
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002079 return FALSE;
Bram Moolenaarc7f02552014-04-01 21:00:59 +02002080 if (path != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002081 *path = vim_strsave((char_u *)fname);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002082 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002083}
2084
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002085#if ((defined(__MINGW32__) || defined (__CYGWIN32__)) && \
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02002086 __MSVCRT_VERSION__ >= 0x800) || (defined(_MSC_VER) && _MSC_VER >= 1400)
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002087/*
2088 * Bad parameter handler.
2089 *
2090 * Certain MS CRT functions will intentionally crash when passed invalid
2091 * parameters to highlight possible security holes. Setting this function as
2092 * the bad parameter handler will prevent the crash.
2093 *
2094 * In debug builds the parameters contain CRT information that might help track
2095 * down the source of a problem, but in non-debug builds the arguments are all
2096 * NULL/0. Debug builds will also produce assert dialogs from the CRT, it is
2097 * worth allowing these to make debugging of issues easier.
2098 */
2099 static void
2100bad_param_handler(const wchar_t *expression,
2101 const wchar_t *function,
2102 const wchar_t *file,
2103 unsigned int line,
2104 uintptr_t pReserved)
2105{
2106}
2107
2108# define SET_INVALID_PARAM_HANDLER \
2109 ((void)_set_invalid_parameter_handler(bad_param_handler))
2110#else
2111# define SET_INVALID_PARAM_HANDLER
2112#endif
2113
Bram Moolenaar071d4272004-06-13 20:20:40 +00002114#ifdef FEAT_GUI_W32
2115
2116/*
2117 * GUI version of mch_init().
2118 */
2119 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002120mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002121{
2122#ifndef __MINGW32__
2123 extern int _fmode;
2124#endif
2125
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002126 /* Silently handle invalid parameters to CRT functions */
2127 SET_INVALID_PARAM_HANDLER;
2128
Bram Moolenaar071d4272004-06-13 20:20:40 +00002129 /* Let critical errors result in a failure, not in a dialog box. Required
2130 * for the timestamp test to work on removed floppies. */
2131 SetErrorMode(SEM_FAILCRITICALERRORS);
2132
2133 _fmode = O_BINARY; /* we do our own CR-LF translation */
2134
2135 /* Specify window size. Is there a place to get the default from? */
2136 Rows = 25;
2137 Columns = 80;
2138
2139 /* Look for 'vimrun' */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002140 {
2141 char_u vimrun_location[_MAX_PATH + 4];
2142
2143 /* First try in same directory as gvim.exe */
2144 STRCPY(vimrun_location, exe_name);
2145 STRCPY(gettail(vimrun_location), "vimrun.exe");
2146 if (mch_getperm(vimrun_location) >= 0)
2147 {
2148 if (*skiptowhite(vimrun_location) != NUL)
2149 {
2150 /* Enclose path with white space in double quotes. */
2151 mch_memmove(vimrun_location + 1, vimrun_location,
2152 STRLEN(vimrun_location) + 1);
2153 *vimrun_location = '"';
2154 STRCPY(gettail(vimrun_location), "vimrun\" ");
2155 }
2156 else
2157 STRCPY(gettail(vimrun_location), "vimrun ");
2158
2159 vimrun_path = (char *)vim_strsave(vimrun_location);
2160 s_dont_use_vimrun = FALSE;
2161 }
Bram Moolenaar43663192017-03-05 14:29:12 +01002162 else if (executable_exists("vimrun.exe", NULL, TRUE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002163 s_dont_use_vimrun = FALSE;
2164
2165 /* Don't give the warning for a missing vimrun.exe right now, but only
2166 * when vimrun was supposed to be used. Don't bother people that do
2167 * not need vimrun.exe. */
2168 if (s_dont_use_vimrun)
2169 need_vimrun_warning = TRUE;
2170 }
2171
2172 /*
2173 * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
2174 * Otherwise the default "findstr /n" is used.
2175 */
Bram Moolenaar43663192017-03-05 14:29:12 +01002176 if (!executable_exists("findstr.exe", NULL, TRUE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002177 set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
2178
2179#ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002180 win_clip_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002181#endif
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01002182
2183 vtp_flag_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002184}
2185
2186
2187#else /* FEAT_GUI_W32 */
2188
2189#define SRWIDTH(sr) ((sr).Right - (sr).Left + 1)
2190#define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1)
2191
2192/*
2193 * ClearConsoleBuffer()
2194 * Description:
2195 * Clears the entire contents of the console screen buffer, using the
2196 * specified attribute.
2197 * Returns:
2198 * TRUE on success
2199 */
2200 static BOOL
2201ClearConsoleBuffer(WORD wAttribute)
2202{
2203 CONSOLE_SCREEN_BUFFER_INFO csbi;
2204 COORD coord;
2205 DWORD NumCells, dummy;
2206
2207 if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2208 return FALSE;
2209
2210 NumCells = csbi.dwSize.X * csbi.dwSize.Y;
2211 coord.X = 0;
2212 coord.Y = 0;
2213 if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells,
2214 coord, &dummy))
2215 {
2216 return FALSE;
2217 }
2218 if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells,
2219 coord, &dummy))
2220 {
2221 return FALSE;
2222 }
2223
2224 return TRUE;
2225}
2226
2227/*
2228 * FitConsoleWindow()
2229 * Description:
2230 * Checks if the console window will fit within given buffer dimensions.
2231 * Also, if requested, will shrink the window to fit.
2232 * Returns:
2233 * TRUE on success
2234 */
2235 static BOOL
2236FitConsoleWindow(
2237 COORD dwBufferSize,
2238 BOOL WantAdjust)
2239{
2240 CONSOLE_SCREEN_BUFFER_INFO csbi;
2241 COORD dwWindowSize;
2242 BOOL NeedAdjust = FALSE;
2243
2244 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2245 {
2246 /*
2247 * A buffer resize will fail if the current console window does
2248 * not lie completely within that buffer. To avoid this, we might
2249 * have to move and possibly shrink the window.
2250 */
2251 if (csbi.srWindow.Right >= dwBufferSize.X)
2252 {
2253 dwWindowSize.X = SRWIDTH(csbi.srWindow);
2254 if (dwWindowSize.X > dwBufferSize.X)
2255 dwWindowSize.X = dwBufferSize.X;
2256 csbi.srWindow.Right = dwBufferSize.X - 1;
2257 csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X;
2258 NeedAdjust = TRUE;
2259 }
2260 if (csbi.srWindow.Bottom >= dwBufferSize.Y)
2261 {
2262 dwWindowSize.Y = SRHEIGHT(csbi.srWindow);
2263 if (dwWindowSize.Y > dwBufferSize.Y)
2264 dwWindowSize.Y = dwBufferSize.Y;
2265 csbi.srWindow.Bottom = dwBufferSize.Y - 1;
2266 csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y;
2267 NeedAdjust = TRUE;
2268 }
2269 if (NeedAdjust && WantAdjust)
2270 {
2271 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow))
2272 return FALSE;
2273 }
2274 return TRUE;
2275 }
2276
2277 return FALSE;
2278}
2279
2280typedef struct ConsoleBufferStruct
2281{
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002282 BOOL IsValid;
2283 CONSOLE_SCREEN_BUFFER_INFO Info;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002284 PCHAR_INFO Buffer;
2285 COORD BufferSize;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002286 PSMALL_RECT Regions;
2287 int NumRegions;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002288} ConsoleBuffer;
2289
2290/*
2291 * SaveConsoleBuffer()
2292 * Description:
2293 * Saves important information about the console buffer, including the
2294 * actual buffer contents. The saved information is suitable for later
2295 * restoration by RestoreConsoleBuffer().
2296 * Returns:
2297 * TRUE if all information was saved; FALSE otherwise
2298 * If FALSE, still sets cb->IsValid if buffer characteristics were saved.
2299 */
2300 static BOOL
2301SaveConsoleBuffer(
2302 ConsoleBuffer *cb)
2303{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002304 DWORD NumCells;
2305 COORD BufferCoord;
2306 SMALL_RECT ReadRegion;
2307 WORD Y, Y_incr;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002308 int i, numregions;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002309
Bram Moolenaar071d4272004-06-13 20:20:40 +00002310 if (cb == NULL)
2311 return FALSE;
2312
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002313 if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002314 {
2315 cb->IsValid = FALSE;
2316 return FALSE;
2317 }
2318 cb->IsValid = TRUE;
2319
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002320 /*
2321 * Allocate a buffer large enough to hold the entire console screen
2322 * buffer. If this ConsoleBuffer structure has already been initialized
2323 * with a buffer of the correct size, then just use that one.
2324 */
2325 if (!cb->IsValid || cb->Buffer == NULL ||
2326 cb->BufferSize.X != cb->Info.dwSize.X ||
2327 cb->BufferSize.Y != cb->Info.dwSize.Y)
2328 {
2329 cb->BufferSize.X = cb->Info.dwSize.X;
2330 cb->BufferSize.Y = cb->Info.dwSize.Y;
2331 NumCells = cb->BufferSize.X * cb->BufferSize.Y;
2332 vim_free(cb->Buffer);
2333 cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
2334 if (cb->Buffer == NULL)
2335 return FALSE;
2336 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002337
2338 /*
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002339 * We will now copy the console screen buffer into our buffer.
2340 * ReadConsoleOutput() seems to be limited as far as how much you
2341 * can read at a time. Empirically, this number seems to be about
2342 * 12000 cells (rows * columns). Start at position (0, 0) and copy
2343 * in chunks until it is all copied. The chunks will all have the
2344 * same horizontal characteristics, so initialize them now. The
2345 * height of each chunk will be (12000 / width).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002346 */
Bram Moolenaar61594242015-09-01 20:23:37 +02002347 BufferCoord.X = 0;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002348 ReadRegion.Left = 0;
2349 ReadRegion.Right = cb->Info.dwSize.X - 1;
2350 Y_incr = 12000 / cb->Info.dwSize.X;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002351
2352 numregions = (cb->Info.dwSize.Y + Y_incr - 1) / Y_incr;
2353 if (cb->Regions == NULL || numregions != cb->NumRegions)
2354 {
2355 cb->NumRegions = numregions;
2356 vim_free(cb->Regions);
2357 cb->Regions = (PSMALL_RECT)alloc(cb->NumRegions * sizeof(SMALL_RECT));
2358 if (cb->Regions == NULL)
2359 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01002360 VIM_CLEAR(cb->Buffer);
Bram Moolenaar444fda22017-08-11 20:37:00 +02002361 return FALSE;
2362 }
2363 }
2364
2365 for (i = 0, Y = 0; i < cb->NumRegions; i++, Y += Y_incr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002366 {
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002367 /*
2368 * Read into position (0, Y) in our buffer.
2369 */
2370 BufferCoord.Y = Y;
2371 /*
2372 * Read the region whose top left corner is (0, Y) and whose bottom
2373 * right corner is (width - 1, Y + Y_incr - 1). This should define
2374 * a region of size width by Y_incr. Don't worry if this region is
2375 * too large for the remaining buffer; it will be cropped.
2376 */
2377 ReadRegion.Top = Y;
2378 ReadRegion.Bottom = Y + Y_incr - 1;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002379 if (!ReadConsoleOutputW(g_hConOut, /* output handle */
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002380 cb->Buffer, /* our buffer */
2381 cb->BufferSize, /* dimensions of our buffer */
2382 BufferCoord, /* offset in our buffer */
2383 &ReadRegion)) /* region to save */
2384 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01002385 VIM_CLEAR(cb->Buffer);
2386 VIM_CLEAR(cb->Regions);
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002387 return FALSE;
2388 }
Bram Moolenaar444fda22017-08-11 20:37:00 +02002389 cb->Regions[i] = ReadRegion;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002390 }
2391
2392 return TRUE;
2393}
2394
2395/*
2396 * RestoreConsoleBuffer()
2397 * Description:
2398 * Restores important information about the console buffer, including the
2399 * actual buffer contents, if desired. The information to restore is in
2400 * the same format used by SaveConsoleBuffer().
2401 * Returns:
2402 * TRUE on success
2403 */
2404 static BOOL
2405RestoreConsoleBuffer(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002406 ConsoleBuffer *cb,
2407 BOOL RestoreScreen)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002408{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002409 COORD BufferCoord;
2410 SMALL_RECT WriteRegion;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002411 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002412
2413 if (cb == NULL || !cb->IsValid)
2414 return FALSE;
2415
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002416 /*
2417 * Before restoring the buffer contents, clear the current buffer, and
2418 * restore the cursor position and window information. Doing this now
2419 * prevents old buffer contents from "flashing" onto the screen.
2420 */
2421 if (RestoreScreen)
2422 ClearConsoleBuffer(cb->Info.wAttributes);
2423
2424 FitConsoleWindow(cb->Info.dwSize, TRUE);
2425 if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
2426 return FALSE;
2427 if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
2428 return FALSE;
2429
2430 if (!RestoreScreen)
2431 {
2432 /*
2433 * No need to restore the screen buffer contents, so we're done.
2434 */
2435 return TRUE;
2436 }
2437
2438 if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
2439 return FALSE;
2440 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
2441 return FALSE;
2442
2443 /*
2444 * Restore the screen buffer contents.
2445 */
2446 if (cb->Buffer != NULL)
2447 {
Bram Moolenaar444fda22017-08-11 20:37:00 +02002448 for (i = 0; i < cb->NumRegions; i++)
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002449 {
Bram Moolenaar444fda22017-08-11 20:37:00 +02002450 BufferCoord.X = cb->Regions[i].Left;
2451 BufferCoord.Y = cb->Regions[i].Top;
2452 WriteRegion = cb->Regions[i];
2453 if (!WriteConsoleOutputW(g_hConOut, /* output handle */
2454 cb->Buffer, /* our buffer */
2455 cb->BufferSize, /* dimensions of our buffer */
2456 BufferCoord, /* offset in our buffer */
2457 &WriteRegion)) /* region to restore */
2458 {
2459 return FALSE;
2460 }
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002461 }
2462 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002463
2464 return TRUE;
2465}
2466
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002467#define FEAT_RESTORE_ORIG_SCREEN
2468#ifdef FEAT_RESTORE_ORIG_SCREEN
2469static ConsoleBuffer g_cbOrig = { 0 };
2470#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002471static ConsoleBuffer g_cbNonTermcap = { 0 };
2472static ConsoleBuffer g_cbTermcap = { 0 };
2473
2474#ifdef FEAT_TITLE
2475#ifdef __BORLANDC__
2476typedef HWND (__stdcall *GETCONSOLEWINDOWPROC)(VOID);
2477#else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002478typedef HWND (WINAPI *GETCONSOLEWINDOWPROC)(VOID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002479#endif
2480char g_szOrigTitle[256] = { 0 };
2481HWND g_hWnd = NULL; /* also used in os_mswin.c */
2482static HICON g_hOrigIconSmall = NULL;
2483static HICON g_hOrigIcon = NULL;
2484static HICON g_hVimIcon = NULL;
2485static BOOL g_fCanChangeIcon = FALSE;
2486
2487/* ICON* are not defined in VC++ 4.0 */
2488#ifndef ICON_SMALL
2489#define ICON_SMALL 0
2490#endif
2491#ifndef ICON_BIG
2492#define ICON_BIG 1
2493#endif
2494/*
2495 * GetConsoleIcon()
2496 * Description:
2497 * Attempts to retrieve the small icon and/or the big icon currently in
2498 * use by a given window.
2499 * Returns:
2500 * TRUE on success
2501 */
2502 static BOOL
2503GetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002504 HWND hWnd,
2505 HICON *phIconSmall,
2506 HICON *phIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002507{
2508 if (hWnd == NULL)
2509 return FALSE;
2510
2511 if (phIconSmall != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002512 *phIconSmall = (HICON)SendMessage(hWnd, WM_GETICON,
2513 (WPARAM)ICON_SMALL, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002514 if (phIcon != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002515 *phIcon = (HICON)SendMessage(hWnd, WM_GETICON,
2516 (WPARAM)ICON_BIG, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002517 return TRUE;
2518}
2519
2520/*
2521 * SetConsoleIcon()
2522 * Description:
2523 * Attempts to change the small icon and/or the big icon currently in
2524 * use by a given window.
2525 * Returns:
2526 * TRUE on success
2527 */
2528 static BOOL
2529SetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002530 HWND hWnd,
2531 HICON hIconSmall,
2532 HICON hIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002533{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002534 if (hWnd == NULL)
2535 return FALSE;
2536
2537 if (hIconSmall != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002538 SendMessage(hWnd, WM_SETICON,
2539 (WPARAM)ICON_SMALL, (LPARAM)hIconSmall);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002540 if (hIcon != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002541 SendMessage(hWnd, WM_SETICON,
2542 (WPARAM)ICON_BIG, (LPARAM) hIcon);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002543 return TRUE;
2544}
2545
2546/*
2547 * SaveConsoleTitleAndIcon()
2548 * Description:
2549 * Saves the current console window title in g_szOrigTitle, for later
2550 * restoration. Also, attempts to obtain a handle to the console window,
2551 * and use it to save the small and big icons currently in use by the
2552 * console window. This is not always possible on some versions of Windows;
2553 * nor is it possible when running Vim remotely using Telnet (since the
2554 * console window the user sees is owned by a remote process).
2555 */
2556 static void
2557SaveConsoleTitleAndIcon(void)
2558{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559 /* Save the original title. */
2560 if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle)))
2561 return;
2562
2563 /*
2564 * Obtain a handle to the console window using GetConsoleWindow() from
2565 * KERNEL32.DLL; we need to handle in order to change the window icon.
2566 * This function only exists on NT-based Windows, starting with Windows
2567 * 2000. On older operating systems, we can't change the window icon
2568 * anyway.
2569 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002570 g_hWnd = GetConsoleWindow();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002571 if (g_hWnd == NULL)
2572 return;
2573
2574 /* Save the original console window icon. */
2575 GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon);
2576 if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL)
2577 return;
2578
2579 /* Extract the first icon contained in the Vim executable. */
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02002580 if (mch_icon_load((HANDLE *)&g_hVimIcon) == FAIL || g_hVimIcon == NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002581 g_hVimIcon = ExtractIcon(NULL, (LPCSTR)exe_name, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002582 if (g_hVimIcon != NULL)
2583 g_fCanChangeIcon = TRUE;
2584}
2585#endif
2586
2587static int g_fWindInitCalled = FALSE;
2588static int g_fTermcapMode = FALSE;
2589static CONSOLE_CURSOR_INFO g_cci;
2590static DWORD g_cmodein = 0;
2591static DWORD g_cmodeout = 0;
2592
2593/*
2594 * non-GUI version of mch_init().
2595 */
2596 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002597mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002599#ifndef FEAT_RESTORE_ORIG_SCREEN
2600 CONSOLE_SCREEN_BUFFER_INFO csbi;
2601#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002602#ifndef __MINGW32__
2603 extern int _fmode;
2604#endif
2605
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002606 /* Silently handle invalid parameters to CRT functions */
2607 SET_INVALID_PARAM_HANDLER;
2608
Bram Moolenaar071d4272004-06-13 20:20:40 +00002609 /* Let critical errors result in a failure, not in a dialog box. Required
2610 * for the timestamp test to work on removed floppies. */
2611 SetErrorMode(SEM_FAILCRITICALERRORS);
2612
2613 _fmode = O_BINARY; /* we do our own CR-LF translation */
2614 out_flush();
2615
2616 /* Obtain handles for the standard Console I/O devices */
2617 if (read_cmd_fd == 0)
2618 g_hConIn = GetStdHandle(STD_INPUT_HANDLE);
2619 else
2620 create_conin();
2621 g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
2622
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002623#ifdef FEAT_RESTORE_ORIG_SCREEN
2624 /* Save the initial console buffer for later restoration */
2625 SaveConsoleBuffer(&g_cbOrig);
2626 g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
2627#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002628 /* Get current text attributes */
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002629 GetConsoleScreenBufferInfo(g_hConOut, &csbi);
2630 g_attrCurrent = g_attrDefault = csbi.wAttributes;
2631#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002632 if (cterm_normal_fg_color == 0)
2633 cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
2634 if (cterm_normal_bg_color == 0)
2635 cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
2636
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02002637 // Fg and Bg color index nunmber at startup
2638 g_color_index_fg = g_attrDefault & 0xf;
2639 g_color_index_bg = (g_attrDefault >> 4) & 0xf;
2640
Bram Moolenaar071d4272004-06-13 20:20:40 +00002641 /* set termcap codes to current text attributes */
2642 update_tcap(g_attrCurrent);
2643
2644 GetConsoleCursorInfo(g_hConOut, &g_cci);
2645 GetConsoleMode(g_hConIn, &g_cmodein);
2646 GetConsoleMode(g_hConOut, &g_cmodeout);
2647
2648#ifdef FEAT_TITLE
2649 SaveConsoleTitleAndIcon();
2650 /*
2651 * Set both the small and big icons of the console window to Vim's icon.
2652 * Note that Vim presently only has one size of icon (32x32), but it
2653 * automatically gets scaled down to 16x16 when setting the small icon.
2654 */
2655 if (g_fCanChangeIcon)
2656 SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon);
2657#endif
2658
2659 ui_get_shellsize();
2660
2661#ifdef MCH_WRITE_DUMP
2662 fdDump = fopen("dump", "wt");
2663
2664 if (fdDump)
2665 {
2666 time_t t;
2667
2668 time(&t);
2669 fputs(ctime(&t), fdDump);
2670 fflush(fdDump);
2671 }
2672#endif
2673
2674 g_fWindInitCalled = TRUE;
2675
2676#ifdef FEAT_MOUSE
2677 g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
2678#endif
2679
2680#ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002681 win_clip_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002682#endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01002683
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01002684 vtp_flag_init();
Bram Moolenaarcafafb32018-02-22 21:07:09 +01002685 vtp_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002686}
2687
2688/*
2689 * non-GUI version of mch_exit().
2690 * Shut down and exit with status `r'
2691 * Careful: mch_exit() may be called before mch_init()!
2692 */
2693 void
2694mch_exit(int r)
2695{
Bram Moolenaar955f1982017-02-05 15:10:51 +01002696 exiting = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002697
Bram Moolenaarcafafb32018-02-22 21:07:09 +01002698 vtp_exit();
2699
Bram Moolenaar955f1982017-02-05 15:10:51 +01002700 stoptermcap();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002701 if (g_fWindInitCalled)
2702 settmode(TMODE_COOK);
2703
2704 ml_close_all(TRUE); /* remove all memfiles */
2705
2706 if (g_fWindInitCalled)
2707 {
2708#ifdef FEAT_TITLE
Bram Moolenaar40385db2018-08-07 22:31:44 +02002709 mch_restore_title(SAVE_RESTORE_BOTH);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002710 /*
2711 * Restore both the small and big icons of the console window to
2712 * what they were at startup. Don't do this when the window is
2713 * closed, Vim would hang here.
2714 */
2715 if (g_fCanChangeIcon && !g_fForceExit)
2716 SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon);
2717#endif
2718
2719#ifdef MCH_WRITE_DUMP
2720 if (fdDump)
2721 {
2722 time_t t;
2723
2724 time(&t);
2725 fputs(ctime(&t), fdDump);
2726 fclose(fdDump);
2727 }
2728 fdDump = NULL;
2729#endif
2730 }
2731
2732 SetConsoleCursorInfo(g_hConOut, &g_cci);
2733 SetConsoleMode(g_hConIn, g_cmodein);
2734 SetConsoleMode(g_hConOut, g_cmodeout);
2735
2736#ifdef DYNAMIC_GETTEXT
2737 dyn_libintl_end();
2738#endif
2739
2740 exit(r);
2741}
2742#endif /* !FEAT_GUI_W32 */
2743
Bram Moolenaar071d4272004-06-13 20:20:40 +00002744/*
2745 * Do we have an interactive window?
2746 */
2747 int
2748mch_check_win(
Bram Moolenaar1266d672017-02-01 13:43:36 +01002749 int argc UNUSED,
2750 char **argv UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002751{
2752 get_exe_name();
2753
2754#ifdef FEAT_GUI_W32
2755 return OK; /* GUI always has a tty */
2756#else
2757 if (isatty(1))
2758 return OK;
2759 return FAIL;
2760#endif
2761}
2762
2763
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002764/*
2765 * fname_casew(): Wide version of fname_case(). Set the case of the file name,
2766 * if it already exists. When "len" is > 0, also expand short to long
2767 * filenames.
2768 * Return FAIL if wide functions are not available, OK otherwise.
2769 * NOTE: much of this is identical to fname_case(), keep in sync!
2770 */
2771 static int
2772fname_casew(
2773 WCHAR *name,
2774 int len)
2775{
2776 WCHAR szTrueName[_MAX_PATH + 2];
2777 WCHAR szTrueNameTemp[_MAX_PATH + 2];
2778 WCHAR *ptrue, *ptruePrev;
2779 WCHAR *porig, *porigPrev;
2780 int flen;
2781 WIN32_FIND_DATAW fb;
Bram Moolenaar73c61632013-12-07 14:48:10 +01002782 HANDLE hFind = INVALID_HANDLE_VALUE;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002783 int c;
2784 int slen;
2785
2786 flen = (int)wcslen(name);
2787 if (flen > _MAX_PATH)
2788 return OK;
2789
2790 /* slash_adjust(name) not needed, already adjusted by fname_case(). */
2791
2792 /* Build the new name in szTrueName[] one component at a time. */
2793 porig = name;
2794 ptrue = szTrueName;
2795
2796 if (iswalpha(porig[0]) && porig[1] == L':')
2797 {
2798 /* copy leading drive letter */
2799 *ptrue++ = *porig++;
2800 *ptrue++ = *porig++;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002801 }
Bram Moolenaar73c61632013-12-07 14:48:10 +01002802 *ptrue = NUL; /* in case nothing follows */
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002803
2804 while (*porig != NUL)
2805 {
2806 /* copy \ characters */
2807 while (*porig == psepc)
2808 *ptrue++ = *porig++;
2809
2810 ptruePrev = ptrue;
2811 porigPrev = porig;
2812 while (*porig != NUL && *porig != psepc)
2813 {
2814 *ptrue++ = *porig++;
2815 }
2816 *ptrue = NUL;
2817
2818 /* To avoid a slow failure append "\*" when searching a directory,
2819 * server or network share. */
2820 wcscpy(szTrueNameTemp, szTrueName);
2821 slen = (int)wcslen(szTrueNameTemp);
2822 if (*porig == psepc && slen + 2 < _MAX_PATH)
2823 wcscpy(szTrueNameTemp + slen, L"\\*");
2824
2825 /* Skip "", "." and "..". */
2826 if (ptrue > ptruePrev
2827 && (ptruePrev[0] != L'.'
2828 || (ptruePrev[1] != NUL
2829 && (ptruePrev[1] != L'.' || ptruePrev[2] != NUL)))
2830 && (hFind = FindFirstFileW(szTrueNameTemp, &fb))
2831 != INVALID_HANDLE_VALUE)
2832 {
2833 c = *porig;
2834 *porig = NUL;
2835
2836 /* Only use the match when it's the same name (ignoring case) or
2837 * expansion is allowed and there is a match with the short name
2838 * and there is enough room. */
2839 if (_wcsicoll(porigPrev, fb.cFileName) == 0
2840 || (len > 0
2841 && (_wcsicoll(porigPrev, fb.cAlternateFileName) == 0
2842 && (int)(ptruePrev - szTrueName)
2843 + (int)wcslen(fb.cFileName) < len)))
2844 {
2845 wcscpy(ptruePrev, fb.cFileName);
2846
2847 /* Look for exact match and prefer it if found. Must be a
2848 * long name, otherwise there would be only one match. */
2849 while (FindNextFileW(hFind, &fb))
2850 {
2851 if (*fb.cAlternateFileName != NUL
2852 && (wcscoll(porigPrev, fb.cFileName) == 0
2853 || (len > 0
2854 && (_wcsicoll(porigPrev,
2855 fb.cAlternateFileName) == 0
2856 && (int)(ptruePrev - szTrueName)
2857 + (int)wcslen(fb.cFileName) < len))))
2858 {
2859 wcscpy(ptruePrev, fb.cFileName);
2860 break;
2861 }
2862 }
2863 }
2864 FindClose(hFind);
2865 *porig = c;
2866 ptrue = ptruePrev + wcslen(ptruePrev);
2867 }
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002868 }
2869
2870 wcscpy(name, szTrueName);
2871 return OK;
2872}
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002873
Bram Moolenaar071d4272004-06-13 20:20:40 +00002874/*
2875 * fname_case(): Set the case of the file name, if it already exists.
2876 * When "len" is > 0, also expand short to long filenames.
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002877 * NOTE: much of this is identical to fname_casew(), keep in sync!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002878 */
2879 void
2880fname_case(
2881 char_u *name,
2882 int len)
2883{
2884 char szTrueName[_MAX_PATH + 2];
Bram Moolenaar464c9252010-10-13 20:37:41 +02002885 char szTrueNameTemp[_MAX_PATH + 2];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002886 char *ptrue, *ptruePrev;
2887 char *porig, *porigPrev;
2888 int flen;
2889 WIN32_FIND_DATA fb;
2890 HANDLE hFind;
2891 int c;
Bram Moolenaar464c9252010-10-13 20:37:41 +02002892 int slen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002893
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +00002894 flen = (int)STRLEN(name);
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002895 if (flen == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002896 return;
2897
2898 slash_adjust(name);
2899
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002900 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2901 {
2902 WCHAR *p = enc_to_utf16(name, NULL);
2903
2904 if (p != NULL)
2905 {
2906 char_u *q;
Bram Moolenaar21d89b62014-10-07 10:38:40 +02002907 WCHAR buf[_MAX_PATH + 1];
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002908
Bram Moolenaar21d89b62014-10-07 10:38:40 +02002909 wcsncpy(buf, p, _MAX_PATH);
2910 buf[_MAX_PATH] = L'\0';
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002911 vim_free(p);
2912
2913 if (fname_casew(buf, (len > 0) ? _MAX_PATH : 0) == OK)
2914 {
2915 q = utf16_to_enc(buf, NULL);
2916 if (q != NULL)
2917 {
2918 vim_strncpy(name, q, (len > 0) ? len - 1 : flen);
2919 vim_free(q);
2920 return;
2921 }
2922 }
2923 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002924 return;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002925 }
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002926
2927 /* If 'enc' is utf-8, flen can be larger than _MAX_PATH.
2928 * So we should check this after calling wide function. */
2929 if (flen > _MAX_PATH)
2930 return;
2931
Bram Moolenaar071d4272004-06-13 20:20:40 +00002932 /* Build the new name in szTrueName[] one component at a time. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002933 porig = (char *)name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002934 ptrue = szTrueName;
2935
2936 if (isalpha(porig[0]) && porig[1] == ':')
2937 {
2938 /* copy leading drive letter */
2939 *ptrue++ = *porig++;
2940 *ptrue++ = *porig++;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002941 }
Bram Moolenaar73c61632013-12-07 14:48:10 +01002942 *ptrue = NUL; /* in case nothing follows */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002943
2944 while (*porig != NUL)
2945 {
2946 /* copy \ characters */
2947 while (*porig == psepc)
2948 *ptrue++ = *porig++;
2949
2950 ptruePrev = ptrue;
2951 porigPrev = porig;
2952 while (*porig != NUL && *porig != psepc)
2953 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002954 int l;
2955
2956 if (enc_dbcs)
2957 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002958 l = (*mb_ptr2len)((char_u *)porig);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002959 while (--l >= 0)
2960 *ptrue++ = *porig++;
2961 }
2962 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002963 *ptrue++ = *porig++;
2964 }
2965 *ptrue = NUL;
2966
Bram Moolenaar464c9252010-10-13 20:37:41 +02002967 /* To avoid a slow failure append "\*" when searching a directory,
2968 * server or network share. */
2969 STRCPY(szTrueNameTemp, szTrueName);
Bram Moolenaar6b5ef062010-10-27 12:18:00 +02002970 slen = (int)strlen(szTrueNameTemp);
Bram Moolenaar464c9252010-10-13 20:37:41 +02002971 if (*porig == psepc && slen + 2 < _MAX_PATH)
2972 STRCPY(szTrueNameTemp + slen, "\\*");
2973
Bram Moolenaar071d4272004-06-13 20:20:40 +00002974 /* Skip "", "." and "..". */
2975 if (ptrue > ptruePrev
2976 && (ptruePrev[0] != '.'
2977 || (ptruePrev[1] != NUL
2978 && (ptruePrev[1] != '.' || ptruePrev[2] != NUL)))
Bram Moolenaar464c9252010-10-13 20:37:41 +02002979 && (hFind = FindFirstFile(szTrueNameTemp, &fb))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002980 != INVALID_HANDLE_VALUE)
2981 {
2982 c = *porig;
2983 *porig = NUL;
2984
2985 /* Only use the match when it's the same name (ignoring case) or
2986 * expansion is allowed and there is a match with the short name
2987 * and there is enough room. */
2988 if (_stricoll(porigPrev, fb.cFileName) == 0
2989 || (len > 0
2990 && (_stricoll(porigPrev, fb.cAlternateFileName) == 0
2991 && (int)(ptruePrev - szTrueName)
2992 + (int)strlen(fb.cFileName) < len)))
2993 {
2994 STRCPY(ptruePrev, fb.cFileName);
2995
2996 /* Look for exact match and prefer it if found. Must be a
2997 * long name, otherwise there would be only one match. */
2998 while (FindNextFile(hFind, &fb))
2999 {
3000 if (*fb.cAlternateFileName != NUL
3001 && (strcoll(porigPrev, fb.cFileName) == 0
3002 || (len > 0
3003 && (_stricoll(porigPrev,
3004 fb.cAlternateFileName) == 0
3005 && (int)(ptruePrev - szTrueName)
3006 + (int)strlen(fb.cFileName) < len))))
3007 {
3008 STRCPY(ptruePrev, fb.cFileName);
3009 break;
3010 }
3011 }
3012 }
3013 FindClose(hFind);
3014 *porig = c;
3015 ptrue = ptruePrev + strlen(ptruePrev);
3016 }
3017 }
3018
3019 STRCPY(name, szTrueName);
3020}
3021
3022
3023/*
3024 * Insert user name in s[len].
3025 */
3026 int
3027mch_get_user_name(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003028 char_u *s,
3029 int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003030{
Bram Moolenaar41a09032007-10-01 18:34:34 +00003031 char szUserName[256 + 1]; /* UNLEN is 256 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003032 DWORD cch = sizeof szUserName;
3033
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01003034 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3035 {
3036 WCHAR wszUserName[256 + 1]; /* UNLEN is 256 */
3037 DWORD wcch = sizeof(wszUserName) / sizeof(WCHAR);
3038
3039 if (GetUserNameW(wszUserName, &wcch))
3040 {
3041 char_u *p = utf16_to_enc(wszUserName, NULL);
3042
3043 if (p != NULL)
3044 {
3045 vim_strncpy(s, p, len - 1);
3046 vim_free(p);
3047 return OK;
3048 }
3049 }
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01003050 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003051 if (GetUserName(szUserName, &cch))
3052 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003053 vim_strncpy(s, (char_u *)szUserName, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003054 return OK;
3055 }
3056 s[0] = NUL;
3057 return FAIL;
3058}
3059
3060
3061/*
3062 * Insert host name in s[len].
3063 */
3064 void
3065mch_get_host_name(
3066 char_u *s,
3067 int len)
3068{
3069 DWORD cch = len;
3070
Bram Moolenaar2cc87382013-12-11 18:21:45 +01003071 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3072 {
3073 WCHAR wszHostName[256 + 1];
3074 DWORD wcch = sizeof(wszHostName) / sizeof(WCHAR);
3075
3076 if (GetComputerNameW(wszHostName, &wcch))
3077 {
3078 char_u *p = utf16_to_enc(wszHostName, NULL);
3079
3080 if (p != NULL)
3081 {
3082 vim_strncpy(s, p, len - 1);
3083 vim_free(p);
3084 return;
3085 }
3086 }
Bram Moolenaar2cc87382013-12-11 18:21:45 +01003087 }
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003088 if (!GetComputerName((LPSTR)s, &cch))
3089 vim_strncpy(s, (char_u *)"PC (Win32 Vim)", len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003090}
3091
3092
3093/*
3094 * return process ID
3095 */
3096 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003097mch_get_pid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003098{
3099 return (long)GetCurrentProcessId();
3100}
3101
3102
3103/*
3104 * Get name of current directory into buffer 'buf' of length 'len' bytes.
3105 * Return OK for success, FAIL for failure.
3106 */
3107 int
3108mch_dirname(
3109 char_u *buf,
3110 int len)
3111{
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003112 char_u abuf[_MAX_PATH + 1];
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003113 DWORD lfnlen;
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003114
Bram Moolenaar071d4272004-06-13 20:20:40 +00003115 /*
3116 * Originally this was:
3117 * return (getcwd(buf, len) != NULL ? OK : FAIL);
3118 * But the Win32s known bug list says that getcwd() doesn't work
3119 * so use the Win32 system call instead. <Negri>
3120 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003121 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3122 {
3123 WCHAR wbuf[_MAX_PATH + 1];
3124
3125 if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0)
3126 {
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003127 WCHAR wcbuf[_MAX_PATH + 1];
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003128 char_u *p = NULL;
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003129
3130 if (GetLongPathNameW(wbuf, wcbuf, _MAX_PATH) != 0)
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003131 {
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003132 p = utf16_to_enc(wcbuf, NULL);
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003133 if (STRLEN(p) >= (size_t)len)
3134 {
3135 // long path name is too long, fall back to short one
3136 vim_free(p);
3137 p = NULL;
3138 }
3139 }
3140 if (p == NULL)
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003141 p = utf16_to_enc(wbuf, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003142
3143 if (p != NULL)
3144 {
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00003145 vim_strncpy(buf, p, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003146 vim_free(p);
3147 return OK;
3148 }
3149 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003150 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003151 }
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003152 if (GetCurrentDirectory(len, (LPSTR)buf) == 0)
3153 return FAIL;
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003154 lfnlen = GetLongPathNameA((LPCSTR)buf, (LPSTR)abuf, _MAX_PATH);
3155 if (lfnlen == 0 || lfnlen >= (DWORD)len)
3156 // Failed to get long path name or it's too long: fall back to the
3157 // short path name.
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003158 return OK;
3159
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003160 STRCPY(buf, abuf);
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003161 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003162}
3163
3164/*
Bram Moolenaarffa22202013-11-21 12:34:11 +01003165 * Get file permissions for "name".
3166 * Return mode_t or -1 for error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003167 */
3168 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003169mch_getperm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003170{
Bram Moolenaar8767f522016-07-01 17:17:39 +02003171 stat_T st;
Bram Moolenaarffa22202013-11-21 12:34:11 +01003172 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003173
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003174 n = mch_stat((char *)name, &st);
Bram Moolenaar78cf3f02014-01-10 18:16:07 +01003175 return n == 0 ? (long)(unsigned short)st.st_mode : -1L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003176}
3177
3178
3179/*
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003180 * Set file permission for "name" to "perm".
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003181 *
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003182 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003183 */
3184 int
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003185mch_setperm(char_u *name, long perm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003186{
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003187 long n = -1;
3188
Bram Moolenaar071d4272004-06-13 20:20:40 +00003189 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3190 {
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003191 WCHAR *p = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003192
3193 if (p != NULL)
3194 {
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003195 n = _wchmod(p, perm);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003196 vim_free(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003197 if (n == -1)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003198 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003199 }
3200 }
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003201 if (n == -1)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003202 n = _chmod((const char *)name, perm);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003203 if (n == -1)
3204 return FAIL;
3205
3206 win32_set_archive(name);
3207
3208 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003209}
3210
3211/*
3212 * Set hidden flag for "name".
3213 */
3214 void
3215mch_hide(char_u *name)
3216{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003217 int attrs = win32_getattrs(name);
3218 if (attrs == -1)
3219 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003220
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003221 attrs |= FILE_ATTRIBUTE_HIDDEN;
3222 win32_setattrs(name, attrs);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003223}
3224
3225/*
Bram Moolenaar8a52ba72015-11-02 14:45:56 +01003226 * Return TRUE if file "name" exists and is hidden.
3227 */
3228 int
3229mch_ishidden(char_u *name)
3230{
3231 int f = win32_getattrs(name);
3232
3233 if (f == -1)
3234 return FALSE; /* file does not exist at all */
3235
3236 return (f & FILE_ATTRIBUTE_HIDDEN) != 0;
3237}
3238
3239/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240 * return TRUE if "name" is a directory
3241 * return FALSE if "name" is not a directory or upon error
3242 */
3243 int
3244mch_isdir(char_u *name)
3245{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003246 int f = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003247
3248 if (f == -1)
3249 return FALSE; /* file does not exist at all */
3250
3251 return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
3252}
3253
3254/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003255 * return TRUE if "name" is a directory, NOT a symlink to a directory
3256 * return FALSE if "name" is not a directory
3257 * return FALSE for error
3258 */
3259 int
3260mch_isrealdir(char_u *name)
3261{
3262 return mch_isdir(name) && !mch_is_symbolic_link(name);
3263}
3264
3265/*
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003266 * Create directory "name".
3267 * Return 0 on success, -1 on error.
3268 */
3269 int
3270mch_mkdir(char_u *name)
3271{
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003272 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3273 {
3274 WCHAR *p;
3275 int retval;
3276
3277 p = enc_to_utf16(name, NULL);
3278 if (p == NULL)
3279 return -1;
3280 retval = _wmkdir(p);
3281 vim_free(p);
3282 return retval;
3283 }
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003284 return _mkdir((const char *)name);
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003285}
3286
3287/*
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003288 * Delete directory "name".
3289 * Return 0 on success, -1 on error.
3290 */
3291 int
3292mch_rmdir(char_u *name)
3293{
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003294 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3295 {
3296 WCHAR *p;
3297 int retval;
3298
3299 p = enc_to_utf16(name, NULL);
3300 if (p == NULL)
3301 return -1;
3302 retval = _wrmdir(p);
3303 vim_free(p);
3304 return retval;
3305 }
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003306 return _rmdir((const char *)name);
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003307}
3308
3309/*
Bram Moolenaar03f48552006-02-28 23:52:23 +00003310 * Return TRUE if file "fname" has more than one link.
3311 */
3312 int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003313mch_is_hard_link(char_u *fname)
Bram Moolenaar03f48552006-02-28 23:52:23 +00003314{
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003315 BY_HANDLE_FILE_INFORMATION info;
3316
3317 return win32_fileinfo(fname, &info) == FILEINFO_OK
3318 && info.nNumberOfLinks > 1;
3319}
3320
3321/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003322 * Return TRUE if "name" is a symbolic link (or a junction).
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003323 */
3324 int
Bram Moolenaar203258c2016-01-17 22:15:16 +01003325mch_is_symbolic_link(char_u *name)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003326{
3327 HANDLE hFind;
3328 int res = FALSE;
3329 WIN32_FIND_DATAA findDataA;
3330 DWORD fileFlags = 0, reparseTag = 0;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003331 WCHAR *wn = NULL;
3332 WIN32_FIND_DATAW findDataW;
3333
3334 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar203258c2016-01-17 22:15:16 +01003335 wn = enc_to_utf16(name, NULL);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003336 if (wn != NULL)
3337 {
3338 hFind = FindFirstFileW(wn, &findDataW);
3339 vim_free(wn);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003340 if (hFind != INVALID_HANDLE_VALUE)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003341 {
3342 fileFlags = findDataW.dwFileAttributes;
3343 reparseTag = findDataW.dwReserved0;
3344 }
3345 }
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003346 else
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003347 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003348 hFind = FindFirstFile((LPCSTR)name, &findDataA);
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003349 if (hFind != INVALID_HANDLE_VALUE)
3350 {
3351 fileFlags = findDataA.dwFileAttributes;
3352 reparseTag = findDataA.dwReserved0;
3353 }
3354 }
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003355
3356 if (hFind != INVALID_HANDLE_VALUE)
3357 FindClose(hFind);
3358
3359 if ((fileFlags & FILE_ATTRIBUTE_REPARSE_POINT)
Bram Moolenaar203258c2016-01-17 22:15:16 +01003360 && (reparseTag == IO_REPARSE_TAG_SYMLINK
3361 || reparseTag == IO_REPARSE_TAG_MOUNT_POINT))
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003362 res = TRUE;
3363
3364 return res;
3365}
3366
3367/*
3368 * Return TRUE if file "fname" has more than one link or if it is a symbolic
3369 * link.
3370 */
3371 int
3372mch_is_linked(char_u *fname)
3373{
3374 if (mch_is_hard_link(fname) || mch_is_symbolic_link(fname))
3375 return TRUE;
3376 return FALSE;
3377}
3378
3379/*
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003380 * Get the by-handle-file-information for "fname".
3381 * Returns FILEINFO_OK when OK.
3382 * returns FILEINFO_ENC_FAIL when enc_to_utf16() failed.
3383 * Returns FILEINFO_READ_FAIL when CreateFile() failed.
3384 * Returns FILEINFO_INFO_FAIL when GetFileInformationByHandle() failed.
3385 */
3386 int
3387win32_fileinfo(char_u *fname, BY_HANDLE_FILE_INFORMATION *info)
3388{
Bram Moolenaar03f48552006-02-28 23:52:23 +00003389 HANDLE hFile;
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003390 int res = FILEINFO_READ_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003391 WCHAR *wn = NULL;
3392
3393 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003394 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00003395 wn = enc_to_utf16(fname, NULL);
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003396 if (wn == NULL)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003397 return FILEINFO_ENC_FAIL;
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003398 }
Bram Moolenaar03f48552006-02-28 23:52:23 +00003399 if (wn != NULL)
3400 {
3401 hFile = CreateFileW(wn, /* file name */
3402 GENERIC_READ, /* access mode */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003403 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003404 NULL, /* security descriptor */
3405 OPEN_EXISTING, /* creation disposition */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003406 FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003407 NULL); /* handle to template file */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003408 vim_free(wn);
Bram Moolenaar03f48552006-02-28 23:52:23 +00003409 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003410 else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003411 hFile = CreateFile((LPCSTR)fname, /* file name */
3412 GENERIC_READ, /* access mode */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003413 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003414 NULL, /* security descriptor */
3415 OPEN_EXISTING, /* creation disposition */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003416 FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003417 NULL); /* handle to template file */
3418
3419 if (hFile != INVALID_HANDLE_VALUE)
3420 {
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003421 if (GetFileInformationByHandle(hFile, info) != 0)
3422 res = FILEINFO_OK;
3423 else
3424 res = FILEINFO_INFO_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003425 CloseHandle(hFile);
3426 }
3427
Bram Moolenaar03f48552006-02-28 23:52:23 +00003428 return res;
3429}
3430
3431/*
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003432 * get file attributes for `name'
3433 * -1 : error
3434 * else FILE_ATTRIBUTE_* defined in winnt.h
3435 */
Bram Moolenaarffa22202013-11-21 12:34:11 +01003436 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003437win32_getattrs(char_u *name)
3438{
3439 int attr;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003440 WCHAR *p = NULL;
3441
3442 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3443 p = enc_to_utf16(name, NULL);
3444
3445 if (p != NULL)
3446 {
3447 attr = GetFileAttributesW(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003448 vim_free(p);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003449 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003450 else
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003451 attr = GetFileAttributes((char *)name);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003452
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003453 return attr;
3454}
3455
3456/*
3457 * set file attributes for `name' to `attrs'
3458 *
3459 * return -1 for failure, 0 otherwise
3460 */
Bram Moolenaar6dff58f2018-09-30 21:43:26 +02003461 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003462win32_setattrs(char_u *name, int attrs)
3463{
3464 int res;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003465 WCHAR *p = NULL;
3466
3467 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3468 p = enc_to_utf16(name, NULL);
3469
3470 if (p != NULL)
3471 {
3472 res = SetFileAttributesW(p, attrs);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003473 vim_free(p);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003474 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003475 else
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003476 res = SetFileAttributes((char *)name, attrs);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003477
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003478 return res ? 0 : -1;
3479}
3480
3481/*
3482 * Set archive flag for "name".
3483 */
Bram Moolenaar6dff58f2018-09-30 21:43:26 +02003484 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003485win32_set_archive(char_u *name)
3486{
3487 int attrs = win32_getattrs(name);
3488 if (attrs == -1)
3489 return -1;
3490
3491 attrs |= FILE_ATTRIBUTE_ARCHIVE;
3492 return win32_setattrs(name, attrs);
3493}
3494
3495/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496 * Return TRUE if file or directory "name" is writable (not readonly).
3497 * Strange semantics of Win32: a readonly directory is writable, but you can't
3498 * delete a file. Let's say this means it is writable.
3499 */
3500 int
3501mch_writable(char_u *name)
3502{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003503 int attrs = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003504
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003505 return (attrs != -1 && (!(attrs & FILE_ATTRIBUTE_READONLY)
3506 || (attrs & FILE_ATTRIBUTE_DIRECTORY)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507}
3508
Bram Moolenaar071d4272004-06-13 20:20:40 +00003509/*
Bram Moolenaar43663192017-03-05 14:29:12 +01003510 * Return TRUE if "name" can be executed, FALSE if not.
Bram Moolenaar77b77102015-03-21 22:18:41 +01003511 * If "use_path" is FALSE only check if "name" is executable.
Bram Moolenaar43663192017-03-05 14:29:12 +01003512 * When returning TRUE and "path" is not NULL save the path and set "*path" to
3513 * the allocated memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003514 */
3515 int
Bram Moolenaar77b77102015-03-21 22:18:41 +01003516mch_can_exe(char_u *name, char_u **path, int use_path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003517{
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003518 char_u buf[_MAX_PATH];
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003519 int len = (int)STRLEN(name);
Bram Moolenaar82956662018-10-06 15:18:45 +02003520 char_u *p, *saved;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003521
3522 if (len >= _MAX_PATH) /* safety check */
3523 return FALSE;
3524
Bram Moolenaar82956662018-10-06 15:18:45 +02003525 /* Ty using the name directly when a Unix-shell like 'shell'. */
3526 if (strstr((char *)gettail(p_sh), "sh") != NULL)
Bram Moolenaar43663192017-03-05 14:29:12 +01003527 if (executable_exists((char *)name, path, use_path))
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003528 return TRUE;
3529
3530 /*
3531 * Loop over all extensions in $PATHEXT.
3532 */
Bram Moolenaar82956662018-10-06 15:18:45 +02003533 p = mch_getenv("PATHEXT");
3534 if (p == NULL)
3535 p = (char_u *)".com;.exe;.bat;.cmd";
3536 saved = vim_strsave(p);
3537 if (saved == NULL)
3538 return FALSE;
3539 p = saved;
3540 while (*p)
3541 {
3542 char_u *tmp = vim_strchr(p, ';');
3543
3544 if (tmp != NULL)
3545 *tmp = NUL;
3546 if (_stricoll((char *)name + len - STRLEN(p), (char *)p) == 0
3547 && executable_exists((char *)name, path, use_path))
3548 {
3549 vim_free(saved);
3550 return TRUE;
3551 }
3552 if (tmp == NULL)
3553 break;
3554 p = tmp + 1;
3555 }
3556 vim_free(saved);
3557
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00003558 vim_strncpy(buf, name, _MAX_PATH - 1);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003559 p = mch_getenv("PATHEXT");
3560 if (p == NULL)
3561 p = (char_u *)".com;.exe;.bat;.cmd";
3562 while (*p)
3563 {
3564 if (p[0] == '.' && (p[1] == NUL || p[1] == ';'))
3565 {
3566 /* A single "." means no extension is added. */
3567 buf[len] = NUL;
3568 ++p;
3569 if (*p)
3570 ++p;
3571 }
3572 else
3573 copy_option_part(&p, buf + len, _MAX_PATH - len, ";");
Bram Moolenaar43663192017-03-05 14:29:12 +01003574 if (executable_exists((char *)buf, path, use_path))
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003575 return TRUE;
3576 }
3577 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003578}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003579
3580/*
3581 * Check what "name" is:
3582 * NODE_NORMAL: file or directory (or doesn't exist)
3583 * NODE_WRITABLE: writable device, socket, fifo, etc.
3584 * NODE_OTHER: non-writable things
3585 */
3586 int
3587mch_nodetype(char_u *name)
3588{
3589 HANDLE hFile;
3590 int type;
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003591 WCHAR *wn = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592
Bram Moolenaar043545e2006-10-10 16:44:07 +00003593 /* We can't open a file with a name "\\.\con" or "\\.\prn" and trying to
3594 * read from it later will cause Vim to hang. Thus return NODE_WRITABLE
3595 * here. */
3596 if (STRNCMP(name, "\\\\.\\", 4) == 0)
3597 return NODE_WRITABLE;
3598
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003599 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003600 wn = enc_to_utf16(name, NULL);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003601
3602 if (wn != NULL)
3603 {
3604 hFile = CreateFileW(wn, /* file name */
3605 GENERIC_WRITE, /* access mode */
3606 0, /* share mode */
3607 NULL, /* security descriptor */
3608 OPEN_EXISTING, /* creation disposition */
3609 0, /* file attributes */
3610 NULL); /* handle to template file */
3611 vim_free(wn);
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003612 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003613 else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003614 hFile = CreateFile((LPCSTR)name, /* file name */
3615 GENERIC_WRITE, /* access mode */
3616 0, /* share mode */
3617 NULL, /* security descriptor */
3618 OPEN_EXISTING, /* creation disposition */
3619 0, /* file attributes */
3620 NULL); /* handle to template file */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003621
3622 if (hFile == INVALID_HANDLE_VALUE)
3623 return NODE_NORMAL;
3624
3625 type = GetFileType(hFile);
3626 CloseHandle(hFile);
3627 if (type == FILE_TYPE_CHAR)
3628 return NODE_WRITABLE;
3629 if (type == FILE_TYPE_DISK)
3630 return NODE_NORMAL;
3631 return NODE_OTHER;
3632}
3633
3634#ifdef HAVE_ACL
3635struct my_acl
3636{
3637 PSECURITY_DESCRIPTOR pSecurityDescriptor;
3638 PSID pSidOwner;
3639 PSID pSidGroup;
3640 PACL pDacl;
3641 PACL pSacl;
3642};
3643#endif
3644
3645/*
3646 * Return a pointer to the ACL of file "fname" in allocated memory.
3647 * Return NULL if the ACL is not available for whatever reason.
3648 */
3649 vim_acl_T
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003650mch_get_acl(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003651{
3652#ifndef HAVE_ACL
3653 return (vim_acl_T)NULL;
3654#else
3655 struct my_acl *p = NULL;
Bram Moolenaar27515922013-06-29 15:36:26 +02003656 DWORD err;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003657
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003658 p = (struct my_acl *)alloc_clear((unsigned)sizeof(struct my_acl));
3659 if (p != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003660 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003661 WCHAR *wn = NULL;
Bram Moolenaar27515922013-06-29 15:36:26 +02003662
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003663 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3664 wn = enc_to_utf16(fname, NULL);
3665 if (wn != NULL)
3666 {
3667 /* Try to retrieve the entire security descriptor. */
3668 err = GetNamedSecurityInfoW(
3669 wn, // Abstract filename
3670 SE_FILE_OBJECT, // File Object
3671 OWNER_SECURITY_INFORMATION |
3672 GROUP_SECURITY_INFORMATION |
3673 DACL_SECURITY_INFORMATION |
3674 SACL_SECURITY_INFORMATION,
3675 &p->pSidOwner, // Ownership information.
3676 &p->pSidGroup, // Group membership.
3677 &p->pDacl, // Discretionary information.
3678 &p->pSacl, // For auditing purposes.
3679 &p->pSecurityDescriptor);
3680 if (err == ERROR_ACCESS_DENIED ||
3681 err == ERROR_PRIVILEGE_NOT_HELD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003682 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003683 /* Retrieve only DACL. */
3684 (void)GetNamedSecurityInfoW(
3685 wn,
3686 SE_FILE_OBJECT,
3687 DACL_SECURITY_INFORMATION,
3688 NULL,
3689 NULL,
3690 &p->pDacl,
3691 NULL,
3692 &p->pSecurityDescriptor);
Bram Moolenaar27515922013-06-29 15:36:26 +02003693 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003694 if (p->pSecurityDescriptor == NULL)
Bram Moolenaar27515922013-06-29 15:36:26 +02003695 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003696 mch_free_acl((vim_acl_T)p);
3697 p = NULL;
3698 }
3699 vim_free(wn);
3700 }
3701 else
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003702 {
3703 /* Try to retrieve the entire security descriptor. */
3704 err = GetNamedSecurityInfo(
3705 (LPSTR)fname, // Abstract filename
3706 SE_FILE_OBJECT, // File Object
3707 OWNER_SECURITY_INFORMATION |
3708 GROUP_SECURITY_INFORMATION |
3709 DACL_SECURITY_INFORMATION |
3710 SACL_SECURITY_INFORMATION,
3711 &p->pSidOwner, // Ownership information.
3712 &p->pSidGroup, // Group membership.
3713 &p->pDacl, // Discretionary information.
3714 &p->pSacl, // For auditing purposes.
3715 &p->pSecurityDescriptor);
3716 if (err == ERROR_ACCESS_DENIED ||
3717 err == ERROR_PRIVILEGE_NOT_HELD)
3718 {
3719 /* Retrieve only DACL. */
3720 (void)GetNamedSecurityInfo(
3721 (LPSTR)fname,
3722 SE_FILE_OBJECT,
3723 DACL_SECURITY_INFORMATION,
3724 NULL,
3725 NULL,
3726 &p->pDacl,
3727 NULL,
3728 &p->pSecurityDescriptor);
3729 }
3730 if (p->pSecurityDescriptor == NULL)
3731 {
3732 mch_free_acl((vim_acl_T)p);
3733 p = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003734 }
3735 }
3736 }
3737
3738 return (vim_acl_T)p;
3739#endif
3740}
3741
Bram Moolenaar27515922013-06-29 15:36:26 +02003742#ifdef HAVE_ACL
3743/*
3744 * Check if "acl" contains inherited ACE.
3745 */
3746 static BOOL
3747is_acl_inherited(PACL acl)
3748{
3749 DWORD i;
3750 ACL_SIZE_INFORMATION acl_info;
3751 PACCESS_ALLOWED_ACE ace;
3752
3753 acl_info.AceCount = 0;
3754 GetAclInformation(acl, &acl_info, sizeof(acl_info), AclSizeInformation);
3755 for (i = 0; i < acl_info.AceCount; i++)
3756 {
3757 GetAce(acl, i, (LPVOID *)&ace);
3758 if (ace->Header.AceFlags & INHERITED_ACE)
3759 return TRUE;
3760 }
3761 return FALSE;
3762}
3763#endif
3764
Bram Moolenaar071d4272004-06-13 20:20:40 +00003765/*
3766 * Set the ACL of file "fname" to "acl" (unless it's NULL).
3767 * Errors are ignored.
3768 * This must only be called with "acl" equal to what mch_get_acl() returned.
3769 */
3770 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003771mch_set_acl(char_u *fname, vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003772{
3773#ifdef HAVE_ACL
3774 struct my_acl *p = (struct my_acl *)acl;
Bram Moolenaar27515922013-06-29 15:36:26 +02003775 SECURITY_INFORMATION sec_info = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003776
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003777 if (p != NULL)
Bram Moolenaar27515922013-06-29 15:36:26 +02003778 {
Bram Moolenaar27515922013-06-29 15:36:26 +02003779 WCHAR *wn = NULL;
Bram Moolenaar27515922013-06-29 15:36:26 +02003780
3781 /* Set security flags */
3782 if (p->pSidOwner)
3783 sec_info |= OWNER_SECURITY_INFORMATION;
3784 if (p->pSidGroup)
3785 sec_info |= GROUP_SECURITY_INFORMATION;
3786 if (p->pDacl)
3787 {
3788 sec_info |= DACL_SECURITY_INFORMATION;
3789 /* Do not inherit its parent's DACL.
3790 * If the DACL is inherited, Cygwin permissions would be changed.
3791 */
3792 if (!is_acl_inherited(p->pDacl))
3793 sec_info |= PROTECTED_DACL_SECURITY_INFORMATION;
3794 }
3795 if (p->pSacl)
3796 sec_info |= SACL_SECURITY_INFORMATION;
3797
Bram Moolenaar27515922013-06-29 15:36:26 +02003798 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3799 wn = enc_to_utf16(fname, NULL);
3800 if (wn != NULL)
3801 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003802 (void)SetNamedSecurityInfoW(
Bram Moolenaar27515922013-06-29 15:36:26 +02003803 wn, // Abstract filename
3804 SE_FILE_OBJECT, // File Object
3805 sec_info,
3806 p->pSidOwner, // Ownership information.
3807 p->pSidGroup, // Group membership.
3808 p->pDacl, // Discretionary information.
3809 p->pSacl // For auditing purposes.
3810 );
3811 vim_free(wn);
3812 }
3813 else
Bram Moolenaar27515922013-06-29 15:36:26 +02003814 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003815 (void)SetNamedSecurityInfo(
Bram Moolenaar27515922013-06-29 15:36:26 +02003816 (LPSTR)fname, // Abstract filename
3817 SE_FILE_OBJECT, // File Object
3818 sec_info,
3819 p->pSidOwner, // Ownership information.
3820 p->pSidGroup, // Group membership.
3821 p->pDacl, // Discretionary information.
3822 p->pSacl // For auditing purposes.
3823 );
3824 }
3825 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003826#endif
3827}
3828
3829 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003830mch_free_acl(vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831{
3832#ifdef HAVE_ACL
3833 struct my_acl *p = (struct my_acl *)acl;
3834
3835 if (p != NULL)
3836 {
3837 LocalFree(p->pSecurityDescriptor); // Free the memory just in case
3838 vim_free(p);
3839 }
3840#endif
3841}
3842
3843#ifndef FEAT_GUI_W32
3844
3845/*
3846 * handler for ctrl-break, ctrl-c interrupts, and fatal events.
3847 */
3848 static BOOL WINAPI
3849handler_routine(
3850 DWORD dwCtrlType)
3851{
Bram Moolenaar589b1102017-08-12 16:39:05 +02003852 INPUT_RECORD ir;
3853 DWORD out;
3854
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855 switch (dwCtrlType)
3856 {
3857 case CTRL_C_EVENT:
3858 if (ctrl_c_interrupts)
3859 g_fCtrlCPressed = TRUE;
3860 return TRUE;
3861
3862 case CTRL_BREAK_EVENT:
3863 g_fCBrkPressed = TRUE;
Bram Moolenaar589b1102017-08-12 16:39:05 +02003864 ctrl_break_was_pressed = TRUE;
3865 /* ReadConsoleInput is blocking, send a key event to continue. */
3866 ir.EventType = KEY_EVENT;
3867 ir.Event.KeyEvent.bKeyDown = TRUE;
3868 ir.Event.KeyEvent.wRepeatCount = 1;
3869 ir.Event.KeyEvent.wVirtualKeyCode = VK_CANCEL;
3870 ir.Event.KeyEvent.wVirtualScanCode = 0;
3871 ir.Event.KeyEvent.dwControlKeyState = 0;
3872 ir.Event.KeyEvent.uChar.UnicodeChar = 0;
3873 WriteConsoleInput(g_hConIn, &ir, 1, &out);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003874 return TRUE;
3875
3876 /* fatal events: shut down gracefully */
3877 case CTRL_CLOSE_EVENT:
3878 case CTRL_LOGOFF_EVENT:
3879 case CTRL_SHUTDOWN_EVENT:
3880 windgoto((int)Rows - 1, 0);
3881 g_fForceExit = TRUE;
3882
Bram Moolenaar0fde2902008-03-16 13:54:13 +00003883 vim_snprintf((char *)IObuff, IOSIZE, _("Vim: Caught %s event\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003884 (dwCtrlType == CTRL_CLOSE_EVENT
3885 ? _("close")
3886 : dwCtrlType == CTRL_LOGOFF_EVENT
3887 ? _("logoff")
3888 : _("shutdown")));
3889#ifdef DEBUG
3890 OutputDebugString(IObuff);
3891#endif
3892
3893 preserve_exit(); /* output IObuff, preserve files and exit */
3894
3895 return TRUE; /* not reached */
3896
3897 default:
3898 return FALSE;
3899 }
3900}
3901
3902
3903/*
3904 * set the tty in (raw) ? "raw" : "cooked" mode
3905 */
3906 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003907mch_settmode(int tmode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003908{
3909 DWORD cmodein;
3910 DWORD cmodeout;
3911 BOOL bEnableHandler;
3912
3913 GetConsoleMode(g_hConIn, &cmodein);
3914 GetConsoleMode(g_hConOut, &cmodeout);
3915 if (tmode == TMODE_RAW)
3916 {
3917 cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3918 ENABLE_ECHO_INPUT);
3919#ifdef FEAT_MOUSE
3920 if (g_fMouseActive)
3921 cmodein |= ENABLE_MOUSE_INPUT;
3922#endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01003923 cmodeout &= ~(
3924#ifdef FEAT_TERMGUICOLORS
3925 /* Do not turn off the ENABLE_PROCESSRD_OUTPUT flag when using
3926 * VTP. */
3927 ((vtp_working) ? 0 : ENABLE_PROCESSED_OUTPUT) |
3928#else
3929 ENABLE_PROCESSED_OUTPUT |
3930#endif
3931 ENABLE_WRAP_AT_EOL_OUTPUT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003932 bEnableHandler = TRUE;
3933 }
3934 else /* cooked */
3935 {
3936 cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3937 ENABLE_ECHO_INPUT);
3938 cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3939 bEnableHandler = FALSE;
3940 }
3941 SetConsoleMode(g_hConIn, cmodein);
3942 SetConsoleMode(g_hConOut, cmodeout);
3943 SetConsoleCtrlHandler(handler_routine, bEnableHandler);
3944
3945#ifdef MCH_WRITE_DUMP
3946 if (fdDump)
3947 {
3948 fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n",
3949 tmode == TMODE_RAW ? "raw" :
3950 tmode == TMODE_COOK ? "cooked" : "normal",
3951 cmodein, cmodeout);
3952 fflush(fdDump);
3953 }
3954#endif
3955}
3956
3957
3958/*
3959 * Get the size of the current window in `Rows' and `Columns'
3960 * Return OK when size could be determined, FAIL otherwise.
3961 */
3962 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003963mch_get_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003964{
3965 CONSOLE_SCREEN_BUFFER_INFO csbi;
3966
3967 if (!g_fTermcapMode && g_cbTermcap.IsValid)
3968 {
3969 /*
3970 * For some reason, we are trying to get the screen dimensions
3971 * even though we are not in termcap mode. The 'Rows' and 'Columns'
3972 * variables are really intended to mean the size of Vim screen
3973 * while in termcap mode.
3974 */
3975 Rows = g_cbTermcap.Info.dwSize.Y;
3976 Columns = g_cbTermcap.Info.dwSize.X;
3977 }
3978 else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3979 {
3980 Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3981 Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3982 }
3983 else
3984 {
3985 Rows = 25;
3986 Columns = 80;
3987 }
3988 return OK;
3989}
3990
3991/*
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02003992 * Resize console buffer to 'COORD'
3993 */
3994 static void
3995ResizeConBuf(
3996 HANDLE hConsole,
3997 COORD coordScreen)
3998{
3999 if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
4000 {
4001#ifdef MCH_WRITE_DUMP
4002 if (fdDump)
4003 {
4004 fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
4005 GetLastError());
4006 fflush(fdDump);
4007 }
4008#endif
4009 }
4010}
4011
4012/*
4013 * Resize console window size to 'srWindowRect'
4014 */
4015 static void
4016ResizeWindow(
4017 HANDLE hConsole,
4018 SMALL_RECT srWindowRect)
4019{
4020 if (!SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect))
4021 {
4022#ifdef MCH_WRITE_DUMP
4023 if (fdDump)
4024 {
4025 fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
4026 GetLastError());
4027 fflush(fdDump);
4028 }
4029#endif
4030 }
4031}
4032
4033/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004034 * Set a console window to `xSize' * `ySize'
4035 */
4036 static void
4037ResizeConBufAndWindow(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004038 HANDLE hConsole,
4039 int xSize,
4040 int ySize)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004041{
4042 CONSOLE_SCREEN_BUFFER_INFO csbi; /* hold current console buffer info */
4043 SMALL_RECT srWindowRect; /* hold the new console size */
4044 COORD coordScreen;
Bram Moolenaar2551c032018-08-23 22:38:31 +02004045 static int resized = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046
4047#ifdef MCH_WRITE_DUMP
4048 if (fdDump)
4049 {
4050 fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
4051 fflush(fdDump);
4052 }
4053#endif
4054
4055 /* get the largest size we can size the console window to */
4056 coordScreen = GetLargestConsoleWindowSize(hConsole);
4057
4058 /* define the new console window size and scroll position */
4059 srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
4060 srWindowRect.Right = (SHORT) (min(xSize, coordScreen.X) - 1);
4061 srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
4062
4063 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
4064 {
4065 int sx, sy;
4066
4067 sx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
4068 sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
4069 if (sy < ySize || sx < xSize)
4070 {
4071 /*
4072 * Increasing number of lines/columns, do buffer first.
4073 * Use the maximal size in x and y direction.
4074 */
4075 if (sy < ySize)
4076 coordScreen.Y = ySize;
4077 else
4078 coordScreen.Y = sy;
4079 if (sx < xSize)
4080 coordScreen.X = xSize;
4081 else
4082 coordScreen.X = sx;
4083 SetConsoleScreenBufferSize(hConsole, coordScreen);
4084 }
4085 }
4086
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02004087 // define the new console buffer size
Bram Moolenaar071d4272004-06-13 20:20:40 +00004088 coordScreen.X = xSize;
4089 coordScreen.Y = ySize;
4090
Bram Moolenaar2551c032018-08-23 22:38:31 +02004091 // In the new console call API, only the first time in reverse order
4092 if (!vtp_working || resized)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004093 {
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02004094 ResizeWindow(hConsole, srWindowRect);
4095 ResizeConBuf(hConsole, coordScreen);
4096 }
4097 else
4098 {
4099 ResizeConBuf(hConsole, coordScreen);
4100 ResizeWindow(hConsole, srWindowRect);
Bram Moolenaar2551c032018-08-23 22:38:31 +02004101 resized = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004102 }
4103}
4104
4105
4106/*
4107 * Set the console window to `Rows' * `Columns'
4108 */
4109 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004110mch_set_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004111{
4112 COORD coordScreen;
4113
4114 /* Don't change window size while still starting up */
4115 if (suppress_winsize != 0)
4116 {
4117 suppress_winsize = 2;
4118 return;
4119 }
4120
4121 if (term_console)
4122 {
4123 coordScreen = GetLargestConsoleWindowSize(g_hConOut);
4124
4125 /* Clamp Rows and Columns to reasonable values */
4126 if (Rows > coordScreen.Y)
4127 Rows = coordScreen.Y;
4128 if (Columns > coordScreen.X)
4129 Columns = coordScreen.X;
4130
4131 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
4132 }
4133}
4134
4135/*
4136 * Rows and/or Columns has changed.
4137 */
4138 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004139mch_new_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004140{
4141 set_scroll_region(0, 0, Columns - 1, Rows - 1);
4142}
4143
4144
4145/*
4146 * Called when started up, to set the winsize that was delayed.
4147 */
4148 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004149mch_set_winsize_now(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004150{
4151 if (suppress_winsize == 2)
4152 {
4153 suppress_winsize = 0;
4154 mch_set_shellsize();
4155 shell_resized();
4156 }
4157 suppress_winsize = 0;
4158}
4159#endif /* FEAT_GUI_W32 */
4160
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004161 static BOOL
4162vim_create_process(
Bram Moolenaar36c85b22013-12-12 20:25:44 +01004163 char *cmd,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004164 BOOL inherit_handles,
Bram Moolenaar3f1138e2014-01-05 13:29:26 +01004165 DWORD flags,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004166 STARTUPINFO *si,
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004167 PROCESS_INFORMATION *pi,
4168 LPVOID *env,
4169 char *cwd)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004170{
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004171 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4172 {
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004173 BOOL ret;
4174 WCHAR *wcmd, *wcwd = NULL;
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004175
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004176 wcmd = enc_to_utf16((char_u *)cmd, NULL);
4177 if (wcmd == NULL)
4178 goto fallback;
4179 if (cwd != NULL)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004180 {
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004181 wcwd = enc_to_utf16((char_u *)cwd, NULL);
4182 if (wcwd == NULL)
4183 {
4184 vim_free(wcmd);
4185 goto fallback;
4186 }
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004187 }
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004188
4189 ret = CreateProcessW(
4190 NULL, /* Executable name */
4191 wcmd, /* Command to execute */
4192 NULL, /* Process security attributes */
4193 NULL, /* Thread security attributes */
4194 inherit_handles, /* Inherit handles */
4195 flags, /* Creation flags */
4196 env, /* Environment */
4197 wcwd, /* Current directory */
4198 (LPSTARTUPINFOW)si, /* Startup information */
4199 pi); /* Process information */
4200 vim_free(wcmd);
4201 if (wcwd != NULL)
4202 vim_free(wcwd);
4203 return ret;
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004204 }
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004205fallback:
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004206 return CreateProcess(
4207 NULL, /* Executable name */
4208 cmd, /* Command to execute */
4209 NULL, /* Process security attributes */
4210 NULL, /* Thread security attributes */
4211 inherit_handles, /* Inherit handles */
4212 flags, /* Creation flags */
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004213 env, /* Environment */
4214 cwd, /* Current directory */
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004215 si, /* Startup information */
4216 pi); /* Process information */
4217}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004218
4219
Bram Moolenaarb2964f22017-03-21 19:29:26 +01004220 static HINSTANCE
4221vim_shell_execute(
4222 char *cmd,
4223 INT n_show_cmd)
4224{
Bram Moolenaarb2964f22017-03-21 19:29:26 +01004225 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4226 {
4227 WCHAR *wcmd = enc_to_utf16((char_u *)cmd, NULL);
4228 if (wcmd != NULL)
4229 {
4230 HINSTANCE ret;
4231 ret = ShellExecuteW(NULL, NULL, wcmd, NULL, NULL, n_show_cmd);
4232 vim_free(wcmd);
4233 return ret;
4234 }
4235 }
Bram Moolenaarb2964f22017-03-21 19:29:26 +01004236 return ShellExecute(NULL, NULL, cmd, NULL, NULL, n_show_cmd);
4237}
4238
4239
Bram Moolenaar071d4272004-06-13 20:20:40 +00004240#if defined(FEAT_GUI_W32) || defined(PROTO)
4241
4242/*
4243 * Specialised version of system() for Win32 GUI mode.
4244 * This version proceeds as follows:
4245 * 1. Create a console window for use by the subprocess
4246 * 2. Run the subprocess (it gets the allocated console by default)
4247 * 3. Wait for the subprocess to terminate and get its exit code
4248 * 4. Prompt the user to press a key to close the console window
4249 */
4250 static int
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004251mch_system_classic(char *cmd, int options)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004252{
4253 STARTUPINFO si;
4254 PROCESS_INFORMATION pi;
4255 DWORD ret = 0;
4256 HWND hwnd = GetFocus();
4257
4258 si.cb = sizeof(si);
4259 si.lpReserved = NULL;
4260 si.lpDesktop = NULL;
4261 si.lpTitle = NULL;
4262 si.dwFlags = STARTF_USESHOWWINDOW;
4263 /*
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004264 * It's nicer to run a filter command in a minimized window.
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004265 * Don't activate the window to keep focus on Vim.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004266 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004267 if (options & SHELL_DOOUT)
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004268 si.wShowWindow = SW_SHOWMINNOACTIVE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004269 else
4270 si.wShowWindow = SW_SHOWNORMAL;
4271 si.cbReserved2 = 0;
4272 si.lpReserved2 = NULL;
4273
Bram Moolenaar071d4272004-06-13 20:20:40 +00004274 /* Now, run the command */
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004275 vim_create_process(cmd, FALSE,
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004276 CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE,
4277 &si, &pi, NULL, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278
4279 /* Wait for the command to terminate before continuing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004280 {
4281#ifdef FEAT_GUI
4282 int delay = 1;
4283
4284 /* Keep updating the window while waiting for the shell to finish. */
4285 for (;;)
4286 {
4287 MSG msg;
4288
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02004289 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004290 {
4291 TranslateMessage(&msg);
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02004292 pDispatchMessage(&msg);
Bram Moolenaare4195c52012-08-02 12:31:44 +02004293 delay = 1;
4294 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004295 }
4296 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4297 break;
4298
4299 /* We start waiting for a very short time and then increase it, so
4300 * that we respond quickly when the process is quick, and don't
4301 * consume too much overhead when it's slow. */
4302 if (delay < 50)
4303 delay += 10;
4304 }
4305#else
4306 WaitForSingleObject(pi.hProcess, INFINITE);
4307#endif
4308
4309 /* Get the command exit code */
4310 GetExitCodeProcess(pi.hProcess, &ret);
4311 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004312
4313 /* Close the handles to the subprocess, so that it goes away */
4314 CloseHandle(pi.hThread);
4315 CloseHandle(pi.hProcess);
4316
4317 /* Try to get input focus back. Doesn't always work though. */
4318 PostMessage(hwnd, WM_SETFOCUS, 0, 0);
4319
4320 return ret;
4321}
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004322
4323/*
4324 * Thread launched by the gui to send the current buffer data to the
4325 * process. This way avoid to hang up vim totally if the children
4326 * process take a long time to process the lines.
4327 */
Bram Moolenaar4c38d662016-08-03 20:54:57 +02004328 static unsigned int __stdcall
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004329sub_process_writer(LPVOID param)
4330{
4331 HANDLE g_hChildStd_IN_Wr = param;
4332 linenr_T lnum = curbuf->b_op_start.lnum;
4333 DWORD len = 0;
4334 DWORD l;
4335 char_u *lp = ml_get(lnum);
4336 char_u *s;
4337 int written = 0;
4338
4339 for (;;)
4340 {
4341 l = (DWORD)STRLEN(lp + written);
4342 if (l == 0)
4343 len = 0;
4344 else if (lp[written] == NL)
4345 {
4346 /* NL -> NUL translation */
4347 WriteFile(g_hChildStd_IN_Wr, "", 1, &len, NULL);
4348 }
4349 else
4350 {
4351 s = vim_strchr(lp + written, NL);
4352 WriteFile(g_hChildStd_IN_Wr, (char *)lp + written,
4353 s == NULL ? l : (DWORD)(s - (lp + written)),
4354 &len, NULL);
4355 }
4356 if (len == (int)l)
4357 {
4358 /* Finished a line, add a NL, unless this line should not have
4359 * one. */
4360 if (lnum != curbuf->b_op_end.lnum
Bram Moolenaar34d72d42015-07-17 14:18:08 +02004361 || (!curbuf->b_p_bin
4362 && curbuf->b_p_fixeol)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004363 || (lnum != curbuf->b_no_eol_lnum
4364 && (lnum != curbuf->b_ml.ml_line_count
4365 || curbuf->b_p_eol)))
4366 {
Bram Moolenaar42335f52018-09-13 15:33:43 +02004367 WriteFile(g_hChildStd_IN_Wr, "\n", 1,
4368 (LPDWORD)&vim_ignored, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004369 }
4370
4371 ++lnum;
4372 if (lnum > curbuf->b_op_end.lnum)
4373 break;
4374
4375 lp = ml_get(lnum);
4376 written = 0;
4377 }
4378 else if (len > 0)
4379 written += len;
4380 }
4381
4382 /* finished all the lines, close pipe */
4383 CloseHandle(g_hChildStd_IN_Wr);
Bram Moolenaar86f2cd52016-08-02 21:55:17 +02004384 return 0;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004385}
4386
4387
4388# define BUFLEN 100 /* length for buffer, stolen from unix version */
4389
4390/*
4391 * This function read from the children's stdout and write the
4392 * data on screen or in the buffer accordingly.
4393 */
4394 static void
4395dump_pipe(int options,
4396 HANDLE g_hChildStd_OUT_Rd,
4397 garray_T *ga,
4398 char_u buffer[],
4399 DWORD *buffer_off)
4400{
4401 DWORD availableBytes = 0;
4402 DWORD i;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004403 int ret;
4404 DWORD len;
4405 DWORD toRead;
4406 int repeatCount;
4407
4408 /* we query the pipe to see if there is any data to read
4409 * to avoid to perform a blocking read */
4410 ret = PeekNamedPipe(g_hChildStd_OUT_Rd, /* pipe to query */
4411 NULL, /* optional buffer */
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02004412 0, /* buffer size */
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004413 NULL, /* number of read bytes */
4414 &availableBytes, /* available bytes total */
4415 NULL); /* byteLeft */
4416
4417 repeatCount = 0;
4418 /* We got real data in the pipe, read it */
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02004419 while (ret != 0 && availableBytes > 0)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004420 {
4421 repeatCount++;
Bram Moolenaara12a1612019-01-24 16:39:02 +01004422 toRead = (DWORD)(BUFLEN - *buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004423 toRead = availableBytes < toRead ? availableBytes : toRead;
Bram Moolenaara12a1612019-01-24 16:39:02 +01004424 ReadFile(g_hChildStd_OUT_Rd, buffer + *buffer_off, toRead , &len, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004425
4426 /* If we haven't read anything, there is a problem */
4427 if (len == 0)
4428 break;
4429
4430 availableBytes -= len;
4431
4432 if (options & SHELL_READ)
4433 {
4434 /* Do NUL -> NL translation, append NL separated
4435 * lines to the current buffer. */
4436 for (i = 0; i < len; ++i)
4437 {
4438 if (buffer[i] == NL)
4439 append_ga_line(ga);
4440 else if (buffer[i] == NUL)
4441 ga_append(ga, NL);
4442 else
4443 ga_append(ga, buffer[i]);
4444 }
4445 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004446 else if (has_mbyte)
4447 {
4448 int l;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004449 int c;
4450 char_u *p;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004451
4452 len += *buffer_off;
4453 buffer[len] = NUL;
4454
4455 /* Check if the last character in buffer[] is
4456 * incomplete, keep these bytes for the next
4457 * round. */
4458 for (p = buffer; p < buffer + len; p += l)
4459 {
Bram Moolenaard3c907b2016-08-17 21:32:09 +02004460 l = MB_CPTR2LEN(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004461 if (l == 0)
4462 l = 1; /* NUL byte? */
4463 else if (MB_BYTE2LEN(*p) != l)
4464 break;
4465 }
4466 if (p == buffer) /* no complete character */
4467 {
4468 /* avoid getting stuck at an illegal byte */
4469 if (len >= 12)
4470 ++p;
4471 else
4472 {
4473 *buffer_off = len;
4474 return;
4475 }
4476 }
4477 c = *p;
4478 *p = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004479 msg_puts((char *)buffer);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004480 if (p < buffer + len)
4481 {
4482 *p = c;
4483 *buffer_off = (DWORD)((buffer + len) - p);
4484 mch_memmove(buffer, p, *buffer_off);
4485 return;
4486 }
4487 *buffer_off = 0;
4488 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004489 else
4490 {
4491 buffer[len] = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004492 msg_puts((char *)buffer);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004493 }
4494
4495 windgoto(msg_row, msg_col);
4496 cursor_on();
4497 out_flush();
4498 }
4499}
4500
4501/*
4502 * Version of system to use for windows NT > 5.0 (Win2K), use pipe
4503 * for communication and doesn't open any new window.
4504 */
4505 static int
4506mch_system_piped(char *cmd, int options)
4507{
4508 STARTUPINFO si;
4509 PROCESS_INFORMATION pi;
4510 DWORD ret = 0;
4511
4512 HANDLE g_hChildStd_IN_Rd = NULL;
4513 HANDLE g_hChildStd_IN_Wr = NULL;
4514 HANDLE g_hChildStd_OUT_Rd = NULL;
4515 HANDLE g_hChildStd_OUT_Wr = NULL;
4516
4517 char_u buffer[BUFLEN + 1]; /* reading buffer + size */
4518 DWORD len;
4519
4520 /* buffer used to receive keys */
4521 char_u ta_buf[BUFLEN + 1]; /* TypeAHead */
4522 int ta_len = 0; /* valid bytes in ta_buf[] */
4523
4524 DWORD i;
4525 int c;
4526 int noread_cnt = 0;
4527 garray_T ga;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004528 int delay = 1;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004529 DWORD buffer_off = 0; /* valid bytes in buffer[] */
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004530 char *p = NULL;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004531
4532 SECURITY_ATTRIBUTES saAttr;
4533
4534 /* Set the bInheritHandle flag so pipe handles are inherited. */
4535 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
4536 saAttr.bInheritHandle = TRUE;
4537 saAttr.lpSecurityDescriptor = NULL;
4538
4539 if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)
4540 /* Ensure the read handle to the pipe for STDOUT is not inherited. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004541 || ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004542 /* Create a pipe for the child process's STDIN. */
4543 || ! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)
4544 /* Ensure the write handle to the pipe for STDIN is not inherited. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004545 || ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004546 {
4547 CloseHandle(g_hChildStd_IN_Rd);
4548 CloseHandle(g_hChildStd_IN_Wr);
4549 CloseHandle(g_hChildStd_OUT_Rd);
4550 CloseHandle(g_hChildStd_OUT_Wr);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004551 msg_puts(_("\nCannot create pipes\n"));
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004552 }
4553
4554 si.cb = sizeof(si);
4555 si.lpReserved = NULL;
4556 si.lpDesktop = NULL;
4557 si.lpTitle = NULL;
4558 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
4559
4560 /* set-up our file redirection */
4561 si.hStdError = g_hChildStd_OUT_Wr;
4562 si.hStdOutput = g_hChildStd_OUT_Wr;
4563 si.hStdInput = g_hChildStd_IN_Rd;
4564 si.wShowWindow = SW_HIDE;
4565 si.cbReserved2 = 0;
4566 si.lpReserved2 = NULL;
4567
4568 if (options & SHELL_READ)
4569 ga_init2(&ga, 1, BUFLEN);
4570
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004571 if (cmd != NULL)
4572 {
4573 p = (char *)vim_strsave((char_u *)cmd);
4574 if (p != NULL)
4575 unescape_shellxquote((char_u *)p, p_sxe);
4576 else
4577 p = cmd;
4578 }
4579
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004580 /* Now, run the command.
4581 * About "Inherit handles" being TRUE: this command can be litigious,
4582 * handle inheritance was deactivated for pending temp file, but, if we
4583 * deactivate it, the pipes don't work for some reason. */
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004584 vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE,
4585 &si, &pi, NULL, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004586
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004587 if (p != cmd)
4588 vim_free(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004589
4590 /* Close our unused side of the pipes */
4591 CloseHandle(g_hChildStd_IN_Rd);
4592 CloseHandle(g_hChildStd_OUT_Wr);
4593
4594 if (options & SHELL_WRITE)
4595 {
Bram Moolenaar86f2cd52016-08-02 21:55:17 +02004596 HANDLE thread = (HANDLE)
4597 _beginthreadex(NULL, /* security attributes */
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004598 0, /* default stack size */
4599 sub_process_writer, /* function to be executed */
4600 g_hChildStd_IN_Wr, /* parameter */
4601 0, /* creation flag, start immediately */
4602 NULL); /* we don't care about thread id */
4603 CloseHandle(thread);
4604 g_hChildStd_IN_Wr = NULL;
4605 }
4606
4607 /* Keep updating the window while waiting for the shell to finish. */
4608 for (;;)
4609 {
4610 MSG msg;
4611
Bram Moolenaar175d0702013-12-11 18:36:33 +01004612 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004613 {
4614 TranslateMessage(&msg);
Bram Moolenaar175d0702013-12-11 18:36:33 +01004615 pDispatchMessage(&msg);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004616 }
4617
4618 /* write pipe information in the window */
4619 if ((options & (SHELL_READ|SHELL_WRITE))
4620# ifdef FEAT_GUI
4621 || gui.in_use
4622# endif
4623 )
4624 {
4625 len = 0;
4626 if (!(options & SHELL_EXPAND)
4627 && ((options &
4628 (SHELL_READ|SHELL_WRITE|SHELL_COOKED))
4629 != (SHELL_READ|SHELL_WRITE|SHELL_COOKED)
4630# ifdef FEAT_GUI
4631 || gui.in_use
4632# endif
4633 )
4634 && (ta_len > 0 || noread_cnt > 4))
4635 {
4636 if (ta_len == 0)
4637 {
4638 /* Get extra characters when we don't have any. Reset the
4639 * counter and timer. */
4640 noread_cnt = 0;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004641 len = ui_inchar(ta_buf, BUFLEN, 10L, 0);
4642 }
4643 if (ta_len > 0 || len > 0)
4644 {
4645 /*
4646 * For pipes: Check for CTRL-C: send interrupt signal to
4647 * child. Check for CTRL-D: EOF, close pipe to child.
4648 */
4649 if (len == 1 && cmd != NULL)
4650 {
4651 if (ta_buf[ta_len] == Ctrl_C)
4652 {
4653 /* Learn what exit code is expected, for
4654 * now put 9 as SIGKILL */
4655 TerminateProcess(pi.hProcess, 9);
4656 }
4657 if (ta_buf[ta_len] == Ctrl_D)
4658 {
4659 CloseHandle(g_hChildStd_IN_Wr);
4660 g_hChildStd_IN_Wr = NULL;
4661 }
4662 }
4663
4664 /* replace K_BS by <BS> and K_DEL by <DEL> */
4665 for (i = ta_len; i < ta_len + len; ++i)
4666 {
4667 if (ta_buf[i] == CSI && len - i > 2)
4668 {
4669 c = TERMCAP2KEY(ta_buf[i + 1], ta_buf[i + 2]);
4670 if (c == K_DEL || c == K_KDEL || c == K_BS)
4671 {
4672 mch_memmove(ta_buf + i + 1, ta_buf + i + 3,
4673 (size_t)(len - i - 2));
4674 if (c == K_DEL || c == K_KDEL)
4675 ta_buf[i] = DEL;
4676 else
4677 ta_buf[i] = Ctrl_H;
4678 len -= 2;
4679 }
4680 }
4681 else if (ta_buf[i] == '\r')
4682 ta_buf[i] = '\n';
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004683 if (has_mbyte)
4684 i += (*mb_ptr2len_len)(ta_buf + i,
4685 ta_len + len - i) - 1;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004686 }
4687
4688 /*
4689 * For pipes: echo the typed characters. For a pty this
4690 * does not seem to work.
4691 */
4692 for (i = ta_len; i < ta_len + len; ++i)
4693 {
4694 if (ta_buf[i] == '\n' || ta_buf[i] == '\b')
4695 msg_putchar(ta_buf[i]);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004696 else if (has_mbyte)
4697 {
4698 int l = (*mb_ptr2len)(ta_buf + i);
4699
4700 msg_outtrans_len(ta_buf + i, l);
4701 i += l - 1;
4702 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004703 else
4704 msg_outtrans_len(ta_buf + i, 1);
4705 }
4706 windgoto(msg_row, msg_col);
4707 out_flush();
4708
4709 ta_len += len;
4710
4711 /*
4712 * Write the characters to the child, unless EOF has been
4713 * typed for pipes. Write one character at a time, to
4714 * avoid losing too much typeahead. When writing buffer
4715 * lines, drop the typed characters (only check for
4716 * CTRL-C).
4717 */
4718 if (options & SHELL_WRITE)
4719 ta_len = 0;
4720 else if (g_hChildStd_IN_Wr != NULL)
4721 {
4722 WriteFile(g_hChildStd_IN_Wr, (char*)ta_buf,
4723 1, &len, NULL);
4724 // if we are typing in, we want to keep things reactive
4725 delay = 1;
4726 if (len > 0)
4727 {
4728 ta_len -= len;
4729 mch_memmove(ta_buf, ta_buf + len, ta_len);
4730 }
4731 }
4732 }
4733 }
4734 }
4735
4736 if (ta_len)
4737 ui_inchar_undo(ta_buf, ta_len);
4738
4739 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4740 {
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004741 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004742 break;
4743 }
4744
4745 ++noread_cnt;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004746 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004747
4748 /* We start waiting for a very short time and then increase it, so
4749 * that we respond quickly when the process is quick, and don't
4750 * consume too much overhead when it's slow. */
4751 if (delay < 50)
4752 delay += 10;
4753 }
4754
4755 /* Close the pipe */
4756 CloseHandle(g_hChildStd_OUT_Rd);
4757 if (g_hChildStd_IN_Wr != NULL)
4758 CloseHandle(g_hChildStd_IN_Wr);
4759
4760 WaitForSingleObject(pi.hProcess, INFINITE);
4761
4762 /* Get the command exit code */
4763 GetExitCodeProcess(pi.hProcess, &ret);
4764
4765 if (options & SHELL_READ)
4766 {
4767 if (ga.ga_len > 0)
4768 {
4769 append_ga_line(&ga);
4770 /* remember that the NL was missing */
4771 curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
4772 }
4773 else
4774 curbuf->b_no_eol_lnum = 0;
4775 ga_clear(&ga);
4776 }
4777
4778 /* Close the handles to the subprocess, so that it goes away */
4779 CloseHandle(pi.hThread);
4780 CloseHandle(pi.hProcess);
4781
4782 return ret;
4783}
4784
4785 static int
4786mch_system(char *cmd, int options)
4787{
4788 /* if we can pipe and the shelltemp option is off */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004789 if (!p_stmp)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004790 return mch_system_piped(cmd, options);
4791 else
4792 return mch_system_classic(cmd, options);
4793}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004794#else
4795
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004796 static int
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01004797mch_system(char *cmd, int options)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004798{
4799 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4800 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004801 WCHAR *wcmd = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004802 if (wcmd != NULL)
4803 {
4804 int ret = _wsystem(wcmd);
4805 vim_free(wcmd);
4806 return ret;
4807 }
4808 }
4809 return system(cmd);
4810}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004811
4812#endif
4813
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004814#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
4815/*
4816 * Use a terminal window to run a shell command in.
4817 */
4818 static int
4819mch_call_shell_terminal(
4820 char_u *cmd,
4821 int options UNUSED) /* SHELL_*, see vim.h */
4822{
4823 jobopt_T opt;
4824 char_u *newcmd = NULL;
4825 typval_T argvar[2];
4826 long_u cmdlen;
4827 int retval = -1;
4828 buf_T *buf;
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004829 job_T *job;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004830 aco_save_T aco;
4831 oparg_T oa; /* operator arguments */
4832
Bram Moolenaar42f652f2018-03-19 21:44:37 +01004833 if (cmd == NULL)
4834 cmdlen = STRLEN(p_sh) + 1;
4835 else
4836 cmdlen = STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004837 newcmd = lalloc(cmdlen, TRUE);
4838 if (newcmd == NULL)
4839 return 255;
Bram Moolenaar42f652f2018-03-19 21:44:37 +01004840 if (cmd == NULL)
4841 {
4842 STRCPY(newcmd, p_sh);
4843 ch_log(NULL, "starting terminal to run a shell");
4844 }
4845 else
4846 {
4847 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s", p_sh, p_shcf, cmd);
4848 ch_log(NULL, "starting terminal for system command '%s'", cmd);
4849 }
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004850
4851 init_job_options(&opt);
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004852
4853 argvar[0].v_type = VAR_STRING;
4854 argvar[0].vval.v_string = newcmd;
4855 argvar[1].v_type = VAR_UNKNOWN;
4856 buf = term_start(argvar, NULL, &opt, TERM_START_SYSTEM);
Bram Moolenaar81c3c89a2018-03-20 11:41:44 +01004857 if (buf == NULL)
4858 return 255;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004859
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004860 job = term_getjob(buf->b_term);
4861 ++job->jv_refcount;
4862
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004863 /* Find a window to make "buf" curbuf. */
4864 aucmd_prepbuf(&aco, buf);
4865
4866 clear_oparg(&oa);
4867 while (term_use_loop())
4868 {
4869 if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
4870 {
4871 /* If terminal_loop() returns OK we got a key that is handled
4872 * in Normal model. We don't do redrawing anyway. */
4873 if (terminal_loop(TRUE) == OK)
4874 normal_cmd(&oa, TRUE);
4875 }
4876 else
4877 normal_cmd(&oa, TRUE);
4878 }
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004879 retval = job->jv_exitval;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004880 ch_log(NULL, "system command finished");
4881
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004882 job_unref(job);
4883
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004884 /* restore curwin/curbuf and a few other things */
4885 aucmd_restbuf(&aco);
4886
4887 wait_return(TRUE);
4888 do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
4889
4890 vim_free(newcmd);
4891 return retval;
4892}
4893#endif
4894
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895/*
4896 * Either execute a command by calling the shell or start a new shell
4897 */
4898 int
4899mch_call_shell(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004900 char_u *cmd,
4901 int options) /* SHELL_*, see vim.h */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004902{
4903 int x = 0;
4904 int tmode = cur_tmode;
4905#ifdef FEAT_TITLE
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004906 char szShellTitle[512];
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004907 int did_set_title = FALSE;
4908
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004909 /* Change the title to reflect that we are in a subshell. */
4910 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4911 {
4912 WCHAR szShellTitle[512];
4913
4914 if (GetConsoleTitleW(szShellTitle,
4915 sizeof(szShellTitle)/sizeof(WCHAR) - 4) > 0)
4916 {
4917 if (cmd == NULL)
4918 wcscat(szShellTitle, L" :sh");
4919 else
4920 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004921 WCHAR *wn = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004922
4923 if (wn != NULL)
4924 {
4925 wcscat(szShellTitle, L" - !");
4926 if ((wcslen(szShellTitle) + wcslen(wn) <
4927 sizeof(szShellTitle)/sizeof(WCHAR)))
4928 wcscat(szShellTitle, wn);
4929 SetConsoleTitleW(szShellTitle);
4930 vim_free(wn);
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004931 did_set_title = TRUE;
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004932 }
4933 }
4934 }
4935 }
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004936 if (!did_set_title)
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004937 /* Change the title to reflect that we are in a subshell. */
4938 if (GetConsoleTitle(szShellTitle, sizeof(szShellTitle) - 4) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004939 {
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004940 if (cmd == NULL)
4941 strcat(szShellTitle, " :sh");
4942 else
4943 {
4944 strcat(szShellTitle, " - !");
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004945 if ((strlen(szShellTitle) + strlen((char *)cmd)
4946 < sizeof(szShellTitle)))
4947 strcat(szShellTitle, (char *)cmd);
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004948 }
4949 SetConsoleTitle(szShellTitle);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004950 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004951#endif
4952
4953 out_flush();
4954
4955#ifdef MCH_WRITE_DUMP
4956 if (fdDump)
4957 {
4958 fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
4959 fflush(fdDump);
4960 }
4961#endif
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004962#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
4963 /* TODO: make the terminal window work with input or output redirected. */
4964 if (vim_strchr(p_go, GO_TERMINAL) != NULL
4965 && (options & (SHELL_FILTER|SHELL_DOOUT|SHELL_WRITE|SHELL_READ)) == 0)
4966 {
4967 /* Use a terminal window to run the command in. */
4968 x = mch_call_shell_terminal(cmd, options);
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01004969# ifdef FEAT_TITLE
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004970 resettitle();
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01004971# endif
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004972 return x;
4973 }
4974#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004975
4976 /*
4977 * Catch all deadly signals while running the external command, because a
4978 * CTRL-C, Ctrl-Break or illegal instruction might otherwise kill us.
4979 */
4980 signal(SIGINT, SIG_IGN);
4981#if defined(__GNUC__) && !defined(__MINGW32__)
4982 signal(SIGKILL, SIG_IGN);
4983#else
4984 signal(SIGBREAK, SIG_IGN);
4985#endif
4986 signal(SIGILL, SIG_IGN);
4987 signal(SIGFPE, SIG_IGN);
4988 signal(SIGSEGV, SIG_IGN);
4989 signal(SIGTERM, SIG_IGN);
4990 signal(SIGABRT, SIG_IGN);
4991
4992 if (options & SHELL_COOKED)
4993 settmode(TMODE_COOK); /* set to normal mode */
4994
4995 if (cmd == NULL)
4996 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004997 x = mch_system((char *)p_sh, options);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004998 }
4999 else
5000 {
5001 /* we use "command" or "cmd" to start the shell; slow but easy */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005002 char_u *newcmd = NULL;
5003 char_u *cmdbase = cmd;
5004 long_u cmdlen;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005005
5006 /* Skip a leading ", ( and "(. */
5007 if (*cmdbase == '"' )
5008 ++cmdbase;
5009 if (*cmdbase == '(')
5010 ++cmdbase;
5011
Bram Moolenaar1c465442017-03-12 20:10:05 +01005012 if ((STRNICMP(cmdbase, "start", 5) == 0) && VIM_ISWHITE(cmdbase[5]))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005013 {
5014 STARTUPINFO si;
5015 PROCESS_INFORMATION pi;
5016 DWORD flags = CREATE_NEW_CONSOLE;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01005017 INT n_show_cmd = SW_SHOWNORMAL;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005018 char_u *p;
5019
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01005020 ZeroMemory(&si, sizeof(si));
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005021 si.cb = sizeof(si);
5022 si.lpReserved = NULL;
5023 si.lpDesktop = NULL;
5024 si.lpTitle = NULL;
5025 si.dwFlags = 0;
5026 si.cbReserved2 = 0;
5027 si.lpReserved2 = NULL;
5028
5029 cmdbase = skipwhite(cmdbase + 5);
5030 if ((STRNICMP(cmdbase, "/min", 4) == 0)
Bram Moolenaar1c465442017-03-12 20:10:05 +01005031 && VIM_ISWHITE(cmdbase[4]))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005032 {
5033 cmdbase = skipwhite(cmdbase + 4);
5034 si.dwFlags = STARTF_USESHOWWINDOW;
5035 si.wShowWindow = SW_SHOWMINNOACTIVE;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01005036 n_show_cmd = SW_SHOWMINNOACTIVE;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005037 }
5038 else if ((STRNICMP(cmdbase, "/b", 2) == 0)
Bram Moolenaar1c465442017-03-12 20:10:05 +01005039 && VIM_ISWHITE(cmdbase[2]))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005040 {
5041 cmdbase = skipwhite(cmdbase + 2);
5042 flags = CREATE_NO_WINDOW;
5043 si.dwFlags = STARTF_USESTDHANDLES;
5044 si.hStdInput = CreateFile("\\\\.\\NUL", // File name
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005045 GENERIC_READ, // Access flags
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005046 0, // Share flags
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005047 NULL, // Security att.
5048 OPEN_EXISTING, // Open flags
5049 FILE_ATTRIBUTE_NORMAL, // File att.
5050 NULL); // Temp file
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005051 si.hStdOutput = si.hStdInput;
5052 si.hStdError = si.hStdInput;
5053 }
5054
5055 /* Remove a trailing ", ) and )" if they have a match
5056 * at the start of the command. */
5057 if (cmdbase > cmd)
5058 {
5059 p = cmdbase + STRLEN(cmdbase);
5060 if (p > cmdbase && p[-1] == '"' && *cmd == '"')
5061 *--p = NUL;
5062 if (p > cmdbase && p[-1] == ')'
5063 && (*cmd =='(' || cmd[1] == '('))
5064 *--p = NUL;
5065 }
5066
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005067 newcmd = cmdbase;
5068 unescape_shellxquote(cmdbase, p_sxe);
5069
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005070 /*
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005071 * If creating new console, arguments are passed to the
5072 * 'cmd.exe' as-is. If it's not, arguments are not treated
5073 * correctly for current 'cmd.exe'. So unescape characters in
5074 * shellxescape except '|' for avoiding to be treated as
5075 * argument to them. Pass the arguments to sub-shell.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005076 */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005077 if (flags != CREATE_NEW_CONSOLE)
5078 {
5079 char_u *subcmd;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01005080 char_u *cmd_shell = mch_getenv("COMSPEC");
5081
5082 if (cmd_shell == NULL || *cmd_shell == NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005083 cmd_shell = (char_u *)default_shell();
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005084
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005085 subcmd = vim_strsave_escaped_ext(cmdbase,
5086 (char_u *)"|", '^', FALSE);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005087 if (subcmd != NULL)
5088 {
5089 /* make "cmd.exe /c arguments" */
5090 cmdlen = STRLEN(cmd_shell) + STRLEN(subcmd) + 5;
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005091 newcmd = lalloc(cmdlen, TRUE);
5092 if (newcmd != NULL)
5093 vim_snprintf((char *)newcmd, cmdlen, "%s /c %s",
Bram Moolenaaree7d1002012-02-22 15:34:08 +01005094 cmd_shell, subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005095 else
5096 newcmd = cmdbase;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01005097 vim_free(subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005098 }
5099 }
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005100
5101 /*
5102 * Now, start the command as a process, so that it doesn't
5103 * inherit our handles which causes unpleasant dangling swap
5104 * files if we exit before the spawned process
5105 */
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005106 if (vim_create_process((char *)newcmd, FALSE, flags,
5107 &si, &pi, NULL, NULL))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005108 x = 0;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01005109 else if (vim_shell_execute((char *)newcmd, n_show_cmd)
5110 > (HINSTANCE)32)
5111 x = 0;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005112 else
5113 {
5114 x = -1;
5115#ifdef FEAT_GUI_W32
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005116 emsg(_("E371: Command not found"));
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005117#endif
5118 }
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005119
5120 if (newcmd != cmdbase)
5121 vim_free(newcmd);
5122
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01005123 if (si.dwFlags == STARTF_USESTDHANDLES && si.hStdInput != NULL)
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005124 {
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01005125 /* Close the handle to \\.\NUL created above. */
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005126 CloseHandle(si.hStdInput);
5127 }
5128 /* Close the handles to the subprocess, so that it goes away */
5129 CloseHandle(pi.hThread);
5130 CloseHandle(pi.hProcess);
5131 }
5132 else
5133 {
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005134 cmdlen = (
Bram Moolenaar071d4272004-06-13 20:20:40 +00005135#ifdef FEAT_GUI_W32
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005136 (!p_stmp ? 0 : STRLEN(vimrun_path)) +
Bram Moolenaar071d4272004-06-13 20:20:40 +00005137#endif
Bram Moolenaar0fde2902008-03-16 13:54:13 +00005138 STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10);
5139
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005140 newcmd = lalloc(cmdlen, TRUE);
5141 if (newcmd != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005142 {
5143#if defined(FEAT_GUI_W32)
5144 if (need_vimrun_warning)
5145 {
Bram Moolenaar63e43442016-11-19 17:28:44 +01005146 char *msg = _("VIMRUN.EXE not found in your $PATH.\n"
5147 "External commands will not pause after completion.\n"
5148 "See :help win32-vimrun for more information.");
5149 char *title = _("Vim Warning");
Bram Moolenaar63e43442016-11-19 17:28:44 +01005150 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
5151 {
5152 WCHAR *wmsg = enc_to_utf16((char_u *)msg, NULL);
5153 WCHAR *wtitle = enc_to_utf16((char_u *)title, NULL);
5154
5155 if (wmsg != NULL && wtitle != NULL)
5156 MessageBoxW(NULL, wmsg, wtitle, MB_ICONWARNING);
5157 vim_free(wmsg);
5158 vim_free(wtitle);
5159 }
5160 else
Bram Moolenaar63e43442016-11-19 17:28:44 +01005161 MessageBox(NULL, msg, title, MB_ICONWARNING);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005162 need_vimrun_warning = FALSE;
5163 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005164 if (!s_dont_use_vimrun && p_stmp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005165 /* Use vimrun to execute the command. It opens a console
5166 * window, which can be closed without killing Vim. */
Bram Moolenaarcc448b32010-07-14 16:52:17 +02005167 vim_snprintf((char *)newcmd, cmdlen, "%s%s%s %s %s",
Bram Moolenaar071d4272004-06-13 20:20:40 +00005168 vimrun_path,
5169 (msg_silent != 0 || (options & SHELL_DOOUT))
5170 ? "-s " : "",
5171 p_sh, p_shcf, cmd);
5172 else
5173#endif
Bram Moolenaarcc448b32010-07-14 16:52:17 +02005174 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s",
Bram Moolenaar0fde2902008-03-16 13:54:13 +00005175 p_sh, p_shcf, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005176 x = mch_system((char *)newcmd, options);
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005177 vim_free(newcmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005178 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005179 }
5180 }
5181
5182 if (tmode == TMODE_RAW)
5183 settmode(TMODE_RAW); /* set to raw mode */
5184
5185 /* Print the return value, unless "vimrun" was used. */
5186 if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent
5187#if defined(FEAT_GUI_W32)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005188 && ((options & SHELL_DOOUT) || s_dont_use_vimrun || !p_stmp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005189#endif
5190 )
5191 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005192 smsg(_("shell returned %d"), x);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005193 msg_putchar('\n');
5194 }
5195#ifdef FEAT_TITLE
5196 resettitle();
5197#endif
5198
5199 signal(SIGINT, SIG_DFL);
5200#if defined(__GNUC__) && !defined(__MINGW32__)
5201 signal(SIGKILL, SIG_DFL);
5202#else
5203 signal(SIGBREAK, SIG_DFL);
5204#endif
5205 signal(SIGILL, SIG_DFL);
5206 signal(SIGFPE, SIG_DFL);
5207 signal(SIGSEGV, SIG_DFL);
5208 signal(SIGTERM, SIG_DFL);
5209 signal(SIGABRT, SIG_DFL);
5210
5211 return x;
5212}
5213
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01005214#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005215 static HANDLE
5216job_io_file_open(
Bram Moolenaar972c3b82017-01-12 21:44:49 +01005217 char_u *fname,
5218 DWORD dwDesiredAccess,
5219 DWORD dwShareMode,
5220 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
5221 DWORD dwCreationDisposition,
5222 DWORD dwFlagsAndAttributes)
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005223{
5224 HANDLE h;
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005225 WCHAR *wn = NULL;
Bram Moolenaara12a1612019-01-24 16:39:02 +01005226
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005227 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
5228 {
Bram Moolenaar972c3b82017-01-12 21:44:49 +01005229 wn = enc_to_utf16(fname, NULL);
5230 if (wn != NULL)
5231 {
5232 h = CreateFileW(wn, dwDesiredAccess, dwShareMode,
5233 lpSecurityAttributes, dwCreationDisposition,
5234 dwFlagsAndAttributes, NULL);
5235 vim_free(wn);
5236 }
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005237 }
5238 if (wn == NULL)
Bram Moolenaar972c3b82017-01-12 21:44:49 +01005239 h = CreateFile((LPCSTR)fname, dwDesiredAccess, dwShareMode,
5240 lpSecurityAttributes, dwCreationDisposition,
5241 dwFlagsAndAttributes, NULL);
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005242 return h;
5243}
5244
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005245/*
5246 * Turn the dictionary "env" into a NUL separated list that can be used as the
5247 * environment argument of vim_create_process().
5248 */
Bram Moolenaarba6febd2017-10-30 21:56:23 +01005249 void
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005250win32_build_env(dict_T *env, garray_T *gap, int is_terminal)
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005251{
5252 hashitem_T *hi;
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005253 long_u todo = env != NULL ? env->dv_hashtab.ht_used : 0;
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005254 LPVOID base = GetEnvironmentStringsW();
5255
5256 /* for last \0 */
5257 if (ga_grow(gap, 1) == FAIL)
5258 return;
5259
5260 if (base)
5261 {
5262 WCHAR *p = (WCHAR*) base;
5263
5264 /* for last \0 */
5265 if (ga_grow(gap, 1) == FAIL)
5266 return;
5267
5268 while (*p != 0 || *(p + 1) != 0)
5269 {
5270 if (ga_grow(gap, 1) == OK)
5271 *((WCHAR*)gap->ga_data + gap->ga_len++) = *p;
5272 p++;
5273 }
5274 FreeEnvironmentStrings(base);
5275 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
5276 }
5277
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005278 if (env != NULL)
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005279 {
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005280 for (hi = env->dv_hashtab.ht_array; todo > 0; ++hi)
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005281 {
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005282 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005283 {
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005284 typval_T *item = &dict_lookup(hi)->di_tv;
5285 WCHAR *wkey = enc_to_utf16((char_u *)hi->hi_key, NULL);
Bram Moolenaard155d7a2018-12-21 16:04:21 +01005286 WCHAR *wval = enc_to_utf16(tv_get_string(item), NULL);
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005287 --todo;
5288 if (wkey != NULL && wval != NULL)
5289 {
5290 size_t n;
5291 size_t lkey = wcslen(wkey);
5292 size_t lval = wcslen(wval);
Bram Moolenaar60104f12017-08-14 23:25:04 +02005293
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005294 if (ga_grow(gap, (int)(lkey + lval + 2)) != OK)
5295 continue;
5296 for (n = 0; n < lkey; n++)
5297 *((WCHAR*)gap->ga_data + gap->ga_len++) = wkey[n];
5298 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'=';
5299 for (n = 0; n < lval; n++)
5300 *((WCHAR*)gap->ga_data + gap->ga_len++) = wval[n];
5301 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
5302 }
5303 if (wkey != NULL) vim_free(wkey);
5304 if (wval != NULL) vim_free(wval);
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005305 }
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005306 }
5307 }
5308
Bram Moolenaar493359e2018-06-12 20:25:52 +02005309# if defined(FEAT_CLIENTSERVER) || defined(FEAT_TERMINAL)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005310 {
Bram Moolenaar493359e2018-06-12 20:25:52 +02005311# ifdef FEAT_CLIENTSERVER
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005312 char_u *servername = get_vim_var_str(VV_SEND_SERVER);
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005313 size_t servername_len = STRLEN(servername);
Bram Moolenaar493359e2018-06-12 20:25:52 +02005314# endif
5315# ifdef FEAT_TERMINAL
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005316 char_u *version = get_vim_var_str(VV_VERSION);
5317 size_t version_len = STRLEN(version);
Bram Moolenaar493359e2018-06-12 20:25:52 +02005318# endif
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005319 // size of "VIM_SERVERNAME=" and value,
5320 // plus "VIM_TERMINAL=" and value,
5321 // plus two terminating NULs
5322 size_t n = 0
Bram Moolenaar493359e2018-06-12 20:25:52 +02005323# ifdef FEAT_CLIENTSERVER
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005324 + 15 + servername_len
Bram Moolenaar493359e2018-06-12 20:25:52 +02005325# endif
5326# ifdef FEAT_TERMINAL
5327 + 13 + version_len + 2
5328# endif
5329 ;
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005330
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005331 if (ga_grow(gap, (int)n) == OK)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005332 {
Bram Moolenaar493359e2018-06-12 20:25:52 +02005333# ifdef FEAT_CLIENTSERVER
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005334 for (n = 0; n < 15; n++)
5335 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5336 (WCHAR)"VIM_SERVERNAME="[n];
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005337 for (n = 0; n < servername_len; n++)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005338 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5339 (WCHAR)servername[n];
5340 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
Bram Moolenaar493359e2018-06-12 20:25:52 +02005341# endif
5342# ifdef FEAT_TERMINAL
5343 if (is_terminal)
5344 {
5345 for (n = 0; n < 13; n++)
5346 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5347 (WCHAR)"VIM_TERMINAL="[n];
5348 for (n = 0; n < version_len; n++)
5349 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5350 (WCHAR)version[n];
5351 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
5352 }
5353# endif
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005354 }
5355 }
Bram Moolenaar79c6b512018-06-12 21:11:12 +02005356# endif
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005357}
5358
Bram Moolenaarb091f302019-01-19 14:37:00 +01005359/*
5360 * Create a pair of pipes.
5361 * Return TRUE for success, FALSE for failure.
5362 */
5363 static BOOL
5364create_pipe_pair(HANDLE handles[2])
5365{
5366 static LONG s;
5367 char name[64];
5368 SECURITY_ATTRIBUTES sa;
5369
5370 sprintf(name, "\\\\?\\pipe\\vim-%08lx-%08lx",
5371 GetCurrentProcessId(),
5372 InterlockedIncrement(&s));
5373
5374 // Create named pipe. Max size of named pipe is 65535.
5375 handles[1] = CreateNamedPipe(
5376 name,
5377 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
5378 PIPE_TYPE_BYTE | PIPE_NOWAIT,
Bram Moolenaar24058382019-01-24 23:11:49 +01005379 1, MAX_NAMED_PIPE_SIZE, 0, 0, NULL);
Bram Moolenaarb091f302019-01-19 14:37:00 +01005380
5381 if (handles[1] == INVALID_HANDLE_VALUE)
5382 return FALSE;
5383
5384 sa.nLength = sizeof(sa);
5385 sa.bInheritHandle = TRUE;
5386 sa.lpSecurityDescriptor = NULL;
5387
5388 handles[0] = CreateFile(name,
5389 FILE_GENERIC_READ,
5390 FILE_SHARE_READ, &sa,
5391 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
5392
5393 if (handles[0] == INVALID_HANDLE_VALUE)
5394 {
5395 CloseHandle(handles[1]);
5396 return FALSE;
5397 }
5398
5399 return TRUE;
5400}
5401
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005402 void
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005403mch_job_start(char *cmd, job_T *job, jobopt_T *options)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005404{
5405 STARTUPINFO si;
5406 PROCESS_INFORMATION pi;
Bram Moolenaar14207f42016-10-27 21:13:10 +02005407 HANDLE jo;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005408 SECURITY_ATTRIBUTES saAttr;
5409 channel_T *channel = NULL;
Bram Moolenaard8070362016-02-15 21:56:54 +01005410 HANDLE ifd[2];
5411 HANDLE ofd[2];
5412 HANDLE efd[2];
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005413 garray_T ga;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005414
5415 int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL;
5416 int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL;
5417 int use_null_for_err = options->jo_io[PART_ERR] == JIO_NULL;
5418 int use_file_for_in = options->jo_io[PART_IN] == JIO_FILE;
5419 int use_file_for_out = options->jo_io[PART_OUT] == JIO_FILE;
5420 int use_file_for_err = options->jo_io[PART_ERR] == JIO_FILE;
5421 int use_out_for_err = options->jo_io[PART_ERR] == JIO_OUT;
5422
5423 if (use_out_for_err && use_null_for_out)
5424 use_null_for_err = TRUE;
Bram Moolenaard8070362016-02-15 21:56:54 +01005425
5426 ifd[0] = INVALID_HANDLE_VALUE;
5427 ifd[1] = INVALID_HANDLE_VALUE;
5428 ofd[0] = INVALID_HANDLE_VALUE;
5429 ofd[1] = INVALID_HANDLE_VALUE;
5430 efd[0] = INVALID_HANDLE_VALUE;
5431 efd[1] = INVALID_HANDLE_VALUE;
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005432 ga_init2(&ga, (int)sizeof(wchar_t), 500);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005433
Bram Moolenaar14207f42016-10-27 21:13:10 +02005434 jo = CreateJobObject(NULL, NULL);
5435 if (jo == NULL)
5436 {
5437 job->jv_status = JOB_FAILED;
5438 goto failed;
5439 }
5440
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005441 if (options->jo_env != NULL)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005442 win32_build_env(options->jo_env, &ga, FALSE);
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005443
Bram Moolenaar76467df2016-02-12 19:30:26 +01005444 ZeroMemory(&pi, sizeof(pi));
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005445 ZeroMemory(&si, sizeof(si));
5446 si.cb = sizeof(si);
Bram Moolenaard8070362016-02-15 21:56:54 +01005447 si.dwFlags |= STARTF_USESHOWWINDOW;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005448 si.wShowWindow = SW_HIDE;
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005449
Bram Moolenaard8070362016-02-15 21:56:54 +01005450 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
5451 saAttr.bInheritHandle = TRUE;
5452 saAttr.lpSecurityDescriptor = NULL;
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005453
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005454 if (use_file_for_in)
5455 {
5456 char_u *fname = options->jo_io_name[PART_IN];
5457
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005458 ifd[0] = job_io_file_open(fname, GENERIC_READ,
5459 FILE_SHARE_READ | FILE_SHARE_WRITE,
5460 &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL);
5461 if (ifd[0] == INVALID_HANDLE_VALUE)
Bram Moolenaar94d01912016-03-08 13:48:51 +01005462 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005463 semsg(_(e_notopen), fname);
Bram Moolenaar94d01912016-03-08 13:48:51 +01005464 goto failed;
5465 }
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005466 }
Bram Moolenaarb091f302019-01-19 14:37:00 +01005467 else if (!use_null_for_in
5468 && (!create_pipe_pair(ifd)
5469 || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005470 goto failed;
5471
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005472 if (use_file_for_out)
5473 {
5474 char_u *fname = options->jo_io_name[PART_OUT];
5475
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005476 ofd[1] = job_io_file_open(fname, GENERIC_WRITE,
5477 FILE_SHARE_READ | FILE_SHARE_WRITE,
5478 &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL);
5479 if (ofd[1] == INVALID_HANDLE_VALUE)
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005480 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005481 semsg(_(e_notopen), fname);
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005482 goto failed;
5483 }
5484 }
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005485 else if (!use_null_for_out &&
5486 (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005487 || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005488 goto failed;
5489
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005490 if (use_file_for_err)
5491 {
5492 char_u *fname = options->jo_io_name[PART_ERR];
5493
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005494 efd[1] = job_io_file_open(fname, GENERIC_WRITE,
5495 FILE_SHARE_READ | FILE_SHARE_WRITE,
5496 &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL);
5497 if (efd[1] == INVALID_HANDLE_VALUE)
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005498 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005499 semsg(_(e_notopen), fname);
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005500 goto failed;
5501 }
5502 }
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005503 else if (!use_out_for_err && !use_null_for_err &&
5504 (!CreatePipe(&efd[0], &efd[1], &saAttr, 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005505 || !SetHandleInformation(efd[0], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaard8070362016-02-15 21:56:54 +01005506 goto failed;
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005507
Bram Moolenaard8070362016-02-15 21:56:54 +01005508 si.dwFlags |= STARTF_USESTDHANDLES;
5509 si.hStdInput = ifd[0];
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005510 si.hStdOutput = ofd[1];
5511 si.hStdError = use_out_for_err ? ofd[1] : efd[1];
5512
5513 if (!use_null_for_in || !use_null_for_out || !use_null_for_err)
5514 {
Bram Moolenaarde279892016-03-11 22:19:44 +01005515 if (options->jo_set & JO_CHANNEL)
5516 {
5517 channel = options->jo_channel;
5518 if (channel != NULL)
5519 ++channel->ch_refcount;
5520 }
5521 else
5522 channel = add_channel();
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005523 if (channel == NULL)
5524 goto failed;
5525 }
Bram Moolenaard8070362016-02-15 21:56:54 +01005526
5527 if (!vim_create_process(cmd, TRUE,
Bram Moolenaar14207f42016-10-27 21:13:10 +02005528 CREATE_SUSPENDED |
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005529 CREATE_DEFAULT_ERROR_MODE |
5530 CREATE_NEW_PROCESS_GROUP |
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005531 CREATE_UNICODE_ENVIRONMENT |
Bram Moolenaar76467df2016-02-12 19:30:26 +01005532 CREATE_NEW_CONSOLE,
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005533 &si, &pi,
5534 ga.ga_data,
5535 (char *)options->jo_cwd))
Bram Moolenaar76467df2016-02-12 19:30:26 +01005536 {
Bram Moolenaar14207f42016-10-27 21:13:10 +02005537 CloseHandle(jo);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005538 job->jv_status = JOB_FAILED;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005539 goto failed;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005540 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005541
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005542 ga_clear(&ga);
5543
Bram Moolenaar14207f42016-10-27 21:13:10 +02005544 if (!AssignProcessToJobObject(jo, pi.hProcess))
5545 {
5546 /* if failing, switch the way to terminate
5547 * process with TerminateProcess. */
5548 CloseHandle(jo);
5549 jo = NULL;
5550 }
5551 ResumeThread(pi.hThread);
Bram Moolenaar75578a32016-03-10 16:33:31 +01005552 CloseHandle(pi.hThread);
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005553 job->jv_proc_info = pi;
Bram Moolenaar14207f42016-10-27 21:13:10 +02005554 job->jv_job_object = jo;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005555 job->jv_status = JOB_STARTED;
5556
Bram Moolenaar641ad6c2016-09-01 18:32:11 +02005557 CloseHandle(ifd[0]);
5558 CloseHandle(ofd[1]);
5559 if (!use_out_for_err && !use_null_for_err)
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005560 CloseHandle(efd[1]);
Bram Moolenaard8070362016-02-15 21:56:54 +01005561
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005562 job->jv_channel = channel;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005563 if (channel != NULL)
5564 {
5565 channel_set_pipes(channel,
5566 use_file_for_in || use_null_for_in
5567 ? INVALID_FD : (sock_T)ifd[1],
5568 use_file_for_out || use_null_for_out
5569 ? INVALID_FD : (sock_T)ofd[0],
5570 use_out_for_err || use_file_for_err || use_null_for_err
5571 ? INVALID_FD : (sock_T)efd[0]);
5572 channel_set_job(channel, job, options);
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005573 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005574 return;
5575
5576failed:
Bram Moolenaard8070362016-02-15 21:56:54 +01005577 CloseHandle(ifd[0]);
5578 CloseHandle(ofd[0]);
5579 CloseHandle(efd[0]);
5580 CloseHandle(ifd[1]);
5581 CloseHandle(ofd[1]);
5582 CloseHandle(efd[1]);
Bram Moolenaarde279892016-03-11 22:19:44 +01005583 channel_unref(channel);
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005584 ga_clear(&ga);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005585}
5586
5587 char *
5588mch_job_status(job_T *job)
5589{
5590 DWORD dwExitCode = 0;
5591
Bram Moolenaar76467df2016-02-12 19:30:26 +01005592 if (!GetExitCodeProcess(job->jv_proc_info.hProcess, &dwExitCode)
5593 || dwExitCode != STILL_ACTIVE)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005594 {
Bram Moolenaareab089d2016-02-21 19:32:02 +01005595 job->jv_exitval = (int)dwExitCode;
Bram Moolenaar7df915d2016-11-17 17:25:32 +01005596 if (job->jv_status < JOB_ENDED)
Bram Moolenaar97792de2016-10-15 18:36:49 +02005597 {
5598 ch_log(job->jv_channel, "Job ended");
5599 job->jv_status = JOB_ENDED;
5600 }
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005601 return "dead";
5602 }
5603 return "run";
5604}
5605
Bram Moolenaar97792de2016-10-15 18:36:49 +02005606 job_T *
5607mch_detect_ended_job(job_T *job_list)
5608{
5609 HANDLE jobHandles[MAXIMUM_WAIT_OBJECTS];
5610 job_T *jobArray[MAXIMUM_WAIT_OBJECTS];
5611 job_T *job = job_list;
5612
5613 while (job != NULL)
5614 {
5615 DWORD n;
5616 DWORD result;
5617
5618 for (n = 0; n < MAXIMUM_WAIT_OBJECTS
5619 && job != NULL; job = job->jv_next)
5620 {
5621 if (job->jv_status == JOB_STARTED)
5622 {
5623 jobHandles[n] = job->jv_proc_info.hProcess;
5624 jobArray[n] = job;
5625 ++n;
5626 }
5627 }
5628 if (n == 0)
5629 continue;
5630 result = WaitForMultipleObjects(n, jobHandles, FALSE, 0);
5631 if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + n)
5632 {
5633 job_T *wait_job = jobArray[result - WAIT_OBJECT_0];
5634
5635 if (STRCMP(mch_job_status(wait_job), "dead") == 0)
5636 return wait_job;
5637 }
5638 }
5639 return NULL;
5640}
5641
Bram Moolenaarfb630902016-10-29 14:55:00 +02005642 static BOOL
5643terminate_all(HANDLE process, int code)
5644{
5645 PROCESSENTRY32 pe;
5646 HANDLE h = INVALID_HANDLE_VALUE;
5647 DWORD pid = GetProcessId(process);
5648
5649 if (pid != 0)
5650 {
5651 h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
5652 if (h != INVALID_HANDLE_VALUE)
5653 {
5654 pe.dwSize = sizeof(PROCESSENTRY32);
5655 if (!Process32First(h, &pe))
5656 goto theend;
5657
5658 do
5659 {
5660 if (pe.th32ParentProcessID == pid)
5661 {
5662 HANDLE ph = OpenProcess(
5663 PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
5664 if (ph != NULL)
5665 {
5666 terminate_all(ph, code);
5667 CloseHandle(ph);
5668 }
5669 }
5670 } while (Process32Next(h, &pe));
5671
5672 CloseHandle(h);
5673 }
5674 }
5675
5676theend:
5677 return TerminateProcess(process, code);
5678}
5679
5680/*
5681 * Send a (deadly) signal to "job".
5682 * Return FAIL if it didn't work.
5683 */
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005684 int
Bram Moolenaar2d33e902017-08-11 16:31:54 +02005685mch_signal_job(job_T *job, char_u *how)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005686{
Bram Moolenaar923d9262016-02-25 20:56:01 +01005687 int ret;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005688
Bram Moolenaar923d9262016-02-25 20:56:01 +01005689 if (STRCMP(how, "term") == 0 || STRCMP(how, "kill") == 0 || *how == NUL)
Bram Moolenaar76467df2016-02-12 19:30:26 +01005690 {
Bram Moolenaarfb630902016-10-29 14:55:00 +02005691 /* deadly signal */
Bram Moolenaar14207f42016-10-27 21:13:10 +02005692 if (job->jv_job_object != NULL)
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01005693 {
5694 if (job->jv_channel != NULL && job->jv_channel->ch_anonymous_pipe)
5695 job->jv_channel->ch_killing = TRUE;
Bram Moolenaar14207f42016-10-27 21:13:10 +02005696 return TerminateJobObject(job->jv_job_object, 0) ? OK : FAIL;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01005697 }
Bram Moolenaarfb630902016-10-29 14:55:00 +02005698 return terminate_all(job->jv_proc_info.hProcess, 0) ? OK : FAIL;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005699 }
5700
5701 if (!AttachConsole(job->jv_proc_info.dwProcessId))
5702 return FAIL;
5703 ret = GenerateConsoleCtrlEvent(
Bram Moolenaar923d9262016-02-25 20:56:01 +01005704 STRCMP(how, "int") == 0 ? CTRL_C_EVENT : CTRL_BREAK_EVENT,
5705 job->jv_proc_info.dwProcessId)
5706 ? OK : FAIL;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005707 FreeConsole();
5708 return ret;
5709}
5710
5711/*
5712 * Clear the data related to "job".
5713 */
5714 void
5715mch_clear_job(job_T *job)
5716{
5717 if (job->jv_status != JOB_FAILED)
5718 {
Bram Moolenaar14207f42016-10-27 21:13:10 +02005719 if (job->jv_job_object != NULL)
5720 CloseHandle(job->jv_job_object);
Bram Moolenaar76467df2016-02-12 19:30:26 +01005721 CloseHandle(job->jv_proc_info.hProcess);
5722 }
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005723}
5724#endif
5725
Bram Moolenaar071d4272004-06-13 20:20:40 +00005726
5727#ifndef FEAT_GUI_W32
5728
5729/*
5730 * Start termcap mode
5731 */
5732 static void
5733termcap_mode_start(void)
5734{
5735 DWORD cmodein;
5736
5737 if (g_fTermcapMode)
5738 return;
5739
5740 SaveConsoleBuffer(&g_cbNonTermcap);
5741
5742 if (g_cbTermcap.IsValid)
5743 {
5744 /*
5745 * We've been in termcap mode before. Restore certain screen
5746 * characteristics, including the buffer size and the window
5747 * size. Since we will be redrawing the screen, we don't need
5748 * to restore the actual contents of the buffer.
5749 */
5750 RestoreConsoleBuffer(&g_cbTermcap, FALSE);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005751 reset_console_color_rgb();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005752 SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow);
5753 Rows = g_cbTermcap.Info.dwSize.Y;
5754 Columns = g_cbTermcap.Info.dwSize.X;
5755 }
5756 else
5757 {
5758 /*
5759 * This is our first time entering termcap mode. Clear the console
5760 * screen buffer, and resize the buffer to match the current window
5761 * size. We will use this as the size of our editing environment.
5762 */
5763 ClearConsoleBuffer(g_attrCurrent);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005764 set_console_color_rgb();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005765 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
5766 }
5767
5768#ifdef FEAT_TITLE
5769 resettitle();
5770#endif
5771
5772 GetConsoleMode(g_hConIn, &cmodein);
5773#ifdef FEAT_MOUSE
5774 if (g_fMouseActive)
5775 cmodein |= ENABLE_MOUSE_INPUT;
5776 else
5777 cmodein &= ~ENABLE_MOUSE_INPUT;
5778#endif
5779 cmodein |= ENABLE_WINDOW_INPUT;
5780 SetConsoleMode(g_hConIn, cmodein);
5781
5782 redraw_later_clear();
5783 g_fTermcapMode = TRUE;
5784}
5785
5786
5787/*
5788 * End termcap mode
5789 */
5790 static void
5791termcap_mode_end(void)
5792{
5793 DWORD cmodein;
5794 ConsoleBuffer *cb;
5795 COORD coord;
5796 DWORD dwDummy;
5797
5798 if (!g_fTermcapMode)
5799 return;
5800
5801 SaveConsoleBuffer(&g_cbTermcap);
5802
5803 GetConsoleMode(g_hConIn, &cmodein);
5804 cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
5805 SetConsoleMode(g_hConIn, cmodein);
5806
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01005807#ifdef FEAT_RESTORE_ORIG_SCREEN
5808 cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
5809#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005810 cb = &g_cbNonTermcap;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01005811#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005812 RestoreConsoleBuffer(cb, p_rs);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005813 reset_console_color_rgb();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005814 SetConsoleCursorInfo(g_hConOut, &g_cci);
5815
5816 if (p_rs || exiting)
5817 {
5818 /*
5819 * Clear anything that happens to be on the current line.
5820 */
5821 coord.X = 0;
5822 coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1));
5823 FillConsoleOutputCharacter(g_hConOut, ' ',
5824 cb->Info.dwSize.X, coord, &dwDummy);
5825 /*
5826 * The following is just for aesthetics. If we are exiting without
5827 * restoring the screen, then we want to have a prompt string
5828 * appear at the bottom line. However, the command interpreter
5829 * seems to always advance the cursor one line before displaying
5830 * the prompt string, which causes the screen to scroll. To
5831 * counter this, move the cursor up one line before exiting.
5832 */
5833 if (exiting && !p_rs)
5834 coord.Y--;
5835 /*
5836 * Position the cursor at the leftmost column of the desired row.
5837 */
5838 SetConsoleCursorPosition(g_hConOut, coord);
5839 }
5840
5841 g_fTermcapMode = FALSE;
5842}
5843#endif /* FEAT_GUI_W32 */
5844
5845
5846#ifdef FEAT_GUI_W32
5847 void
5848mch_write(
Bram Moolenaar1266d672017-02-01 13:43:36 +01005849 char_u *s UNUSED,
5850 int len UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005851{
5852 /* never used */
5853}
5854
5855#else
5856
5857/*
5858 * clear `n' chars, starting from `coord'
5859 */
5860 static void
5861clear_chars(
5862 COORD coord,
5863 DWORD n)
5864{
5865 DWORD dwDummy;
5866
5867 FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005868
5869 if (!USE_VTP)
5870 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy);
5871 else
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02005872 {
5873 set_console_color_rgb();
5874 gotoxy(coord.X + 1, coord.Y + 1);
5875 vtp_printf("\033[%dX", n);
5876 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005877}
5878
5879
5880/*
5881 * Clear the screen
5882 */
5883 static void
5884clear_screen(void)
5885{
5886 g_coord.X = g_coord.Y = 0;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005887
5888 if (!USE_VTP)
5889 clear_chars(g_coord, Rows * Columns);
5890 else
5891 {
5892 set_console_color_rgb();
5893 gotoxy(1, 1);
5894 vtp_printf("\033[2J");
5895 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005896}
5897
5898
5899/*
5900 * Clear to end of display
5901 */
5902 static void
5903clear_to_end_of_display(void)
5904{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005905 COORD save = g_coord;
5906
5907 if (!USE_VTP)
5908 clear_chars(g_coord, (Rows - g_coord.Y - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005909 * Columns + (Columns - g_coord.X));
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005910 else
5911 {
5912 set_console_color_rgb();
5913 gotoxy(g_coord.X + 1, g_coord.Y + 1);
5914 vtp_printf("\033[0J");
5915
5916 gotoxy(save.X + 1, save.Y + 1);
5917 g_coord = save;
5918 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005919}
5920
5921
5922/*
5923 * Clear to end of line
5924 */
5925 static void
5926clear_to_end_of_line(void)
5927{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005928 COORD save = g_coord;
5929
5930 if (!USE_VTP)
5931 clear_chars(g_coord, Columns - g_coord.X);
5932 else
5933 {
5934 set_console_color_rgb();
5935 gotoxy(g_coord.X + 1, g_coord.Y + 1);
5936 vtp_printf("\033[0K");
5937
5938 gotoxy(save.X + 1, save.Y + 1);
5939 g_coord = save;
5940 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005941}
5942
5943
5944/*
5945 * Scroll the scroll region up by `cLines' lines
5946 */
5947 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005948scroll(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005949{
5950 COORD oldcoord = g_coord;
5951
5952 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
5953 delete_lines(cLines);
5954
5955 g_coord = oldcoord;
5956}
5957
5958
5959/*
5960 * Set the scroll region
5961 */
5962 static void
5963set_scroll_region(
5964 unsigned left,
5965 unsigned top,
5966 unsigned right,
5967 unsigned bottom)
5968{
5969 if (left >= right
5970 || top >= bottom
5971 || right > (unsigned) Columns - 1
5972 || bottom > (unsigned) Rows - 1)
5973 return;
5974
5975 g_srScrollRegion.Left = left;
5976 g_srScrollRegion.Top = top;
5977 g_srScrollRegion.Right = right;
5978 g_srScrollRegion.Bottom = bottom;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005979
5980 if (USE_VTP)
5981 vtp_printf("\033[%d;%dr", top + 1, bottom + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005982}
5983
5984
5985/*
5986 * Insert `cLines' lines at the current cursor position
5987 */
5988 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005989insert_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005990{
5991 SMALL_RECT source;
5992 COORD dest;
5993 CHAR_INFO fill;
5994
5995 dest.X = 0;
5996 dest.Y = g_coord.Y + cLines;
5997
5998 source.Left = 0;
5999 source.Top = g_coord.Y;
6000 source.Right = g_srScrollRegion.Right;
6001 source.Bottom = g_srScrollRegion.Bottom - cLines;
6002
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006003 if (!USE_VTP)
6004 {
6005 fill.Char.AsciiChar = ' ';
6006 fill.Attributes = g_attrCurrent;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006007
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006008 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
6009 }
6010 else
6011 {
6012 set_console_color_rgb();
6013
6014 gotoxy(1, source.Top + 1);
6015 vtp_printf("\033[%dT", cLines);
6016 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006017
6018 /* Here we have to deal with a win32 console flake: If the scroll
6019 * region looks like abc and we scroll c to a and fill with d we get
6020 * cbd... if we scroll block c one line at a time to a, we get cdd...
6021 * vim expects cdd consistently... So we have to deal with that
6022 * here... (this also occurs scrolling the same way in the other
6023 * direction). */
6024
6025 if (source.Bottom < dest.Y)
6026 {
6027 COORD coord;
6028
6029 coord.X = 0;
6030 coord.Y = source.Bottom;
6031 clear_chars(coord, Columns * (dest.Y - source.Bottom));
6032 }
6033}
6034
6035
6036/*
6037 * Delete `cLines' lines at the current cursor position
6038 */
6039 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006040delete_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006041{
6042 SMALL_RECT source;
6043 COORD dest;
6044 CHAR_INFO fill;
6045 int nb;
6046
6047 dest.X = 0;
6048 dest.Y = g_coord.Y;
6049
6050 source.Left = 0;
6051 source.Top = g_coord.Y + cLines;
6052 source.Right = g_srScrollRegion.Right;
6053 source.Bottom = g_srScrollRegion.Bottom;
6054
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006055 if (!USE_VTP)
6056 {
6057 fill.Char.AsciiChar = ' ';
6058 fill.Attributes = g_attrCurrent;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006059
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006060 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
6061 }
6062 else
6063 {
6064 set_console_color_rgb();
6065
6066 gotoxy(1, source.Top + 1);
6067 vtp_printf("\033[%dS", cLines);
6068 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006069
6070 /* Here we have to deal with a win32 console flake: If the scroll
6071 * region looks like abc and we scroll c to a and fill with d we get
6072 * cbd... if we scroll block c one line at a time to a, we get cdd...
6073 * vim expects cdd consistently... So we have to deal with that
6074 * here... (this also occurs scrolling the same way in the other
6075 * direction). */
6076
6077 nb = dest.Y + (source.Bottom - source.Top) + 1;
6078
6079 if (nb < source.Top)
6080 {
6081 COORD coord;
6082
6083 coord.X = 0;
6084 coord.Y = nb;
6085 clear_chars(coord, Columns * (source.Top - nb));
6086 }
6087}
6088
6089
6090/*
6091 * Set the cursor position
6092 */
6093 static void
6094gotoxy(
6095 unsigned x,
6096 unsigned y)
6097{
6098 if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows)
6099 return;
6100
6101 /* external cursor coords are 1-based; internal are 0-based */
6102 g_coord.X = x - 1;
6103 g_coord.Y = y - 1;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006104
6105 if (!USE_VTP)
6106 SetConsoleCursorPosition(g_hConOut, g_coord);
6107 else
6108 vtp_printf("\033[%d;%dH", y, x);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006109}
6110
6111
6112/*
6113 * Set the current text attribute = (foreground | background)
6114 * See ../doc/os_win32.txt for the numbers.
6115 */
6116 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006117textattr(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006118{
Bram Moolenaar6383b922015-03-24 17:12:19 +01006119 g_attrCurrent = wAttr & 0xff;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006120
6121 SetConsoleTextAttribute(g_hConOut, wAttr);
6122}
6123
6124
6125 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006126textcolor(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006127{
Bram Moolenaar6383b922015-03-24 17:12:19 +01006128 g_attrCurrent = (g_attrCurrent & 0xf0) + (wAttr & 0x0f);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006129
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006130 if (!USE_VTP)
6131 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
6132 else
6133 vtp_sgr_bulk(wAttr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006134}
6135
6136
6137 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006138textbackground(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006139{
Bram Moolenaar6383b922015-03-24 17:12:19 +01006140 g_attrCurrent = (g_attrCurrent & 0x0f) + ((wAttr & 0x0f) << 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006141
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006142 if (!USE_VTP)
6143 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
6144 else
6145 vtp_sgr_bulk(wAttr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006146}
6147
6148
6149/*
6150 * restore the default text attribute (whatever we started with)
6151 */
6152 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006153normvideo(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006154{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006155 if (!USE_VTP)
6156 textattr(g_attrDefault);
6157 else
6158 vtp_sgr_bulk(0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006159}
6160
6161
6162static WORD g_attrPreStandout = 0;
6163
6164/*
6165 * Make the text standout, by brightening it
6166 */
6167 static void
6168standout(void)
6169{
6170 g_attrPreStandout = g_attrCurrent;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006171
Bram Moolenaar071d4272004-06-13 20:20:40 +00006172 textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
6173}
6174
6175
6176/*
6177 * Turn off standout mode
6178 */
6179 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006180standend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006181{
6182 if (g_attrPreStandout)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006183 textattr(g_attrPreStandout);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006184
6185 g_attrPreStandout = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006186}
6187
6188
6189/*
Bram Moolenaarff1d0d42007-05-10 17:24:16 +00006190 * Set normal fg/bg color, based on T_ME. Called when t_me has been set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006191 */
6192 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006193mch_set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006194{
6195 char_u *p;
6196 int n;
6197
6198 cterm_normal_fg_color = (g_attrDefault & 0xf) + 1;
6199 cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006200 if (
6201#ifdef FEAT_TERMGUICOLORS
6202 !p_tgc &&
6203#endif
6204 T_ME[0] == ESC && T_ME[1] == '|')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006205 {
6206 p = T_ME + 2;
6207 n = getdigits(&p);
6208 if (*p == 'm' && n > 0)
6209 {
6210 cterm_normal_fg_color = (n & 0xf) + 1;
6211 cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
6212 }
6213 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006214#ifdef FEAT_TERMGUICOLORS
6215 cterm_normal_fg_gui_color = INVALCOLOR;
6216 cterm_normal_bg_gui_color = INVALCOLOR;
6217#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006218}
6219
6220
6221/*
6222 * visual bell: flash the screen
6223 */
6224 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006225visual_bell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006226{
6227 COORD coordOrigin = {0, 0};
6228 WORD attrFlash = ~g_attrCurrent & 0xff;
6229
6230 DWORD dwDummy;
6231 LPWORD oldattrs = (LPWORD)alloc(Rows * Columns * sizeof(WORD));
6232
6233 if (oldattrs == NULL)
6234 return;
6235 ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
6236 coordOrigin, &dwDummy);
6237 FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns,
6238 coordOrigin, &dwDummy);
6239
6240 Sleep(15); /* wait for 15 msec */
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006241 if (!USE_VTP)
6242 WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006243 coordOrigin, &dwDummy);
6244 vim_free(oldattrs);
6245}
6246
6247
6248/*
6249 * Make the cursor visible or invisible
6250 */
6251 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006252cursor_visible(BOOL fVisible)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006253{
6254 s_cursor_visible = fVisible;
6255#ifdef MCH_CURSOR_SHAPE
6256 mch_update_cursor();
6257#endif
6258}
6259
6260
6261/*
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006262 * Write "cbToWrite" bytes in `pchBuf' to the screen.
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006263 * Returns the number of bytes actually written (at least one).
Bram Moolenaar071d4272004-06-13 20:20:40 +00006264 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006265 static DWORD
Bram Moolenaar071d4272004-06-13 20:20:40 +00006266write_chars(
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006267 char_u *pchBuf,
6268 DWORD cbToWrite)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006269{
6270 COORD coord = g_coord;
6271 DWORD written;
6272
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006273 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6274 {
6275 static WCHAR *unicodebuf = NULL;
6276 static int unibuflen = 0;
6277 int length;
6278 DWORD n, cchwritten, cells;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006279
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006280 length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite, 0, 0);
6281 if (unicodebuf == NULL || length > unibuflen)
6282 {
6283 vim_free(unicodebuf);
6284 unicodebuf = (WCHAR *)lalloc(length * sizeof(WCHAR), FALSE);
6285 unibuflen = length;
6286 }
6287 MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite,
6288 unicodebuf, unibuflen);
6289
6290 cells = mb_string2cells(pchBuf, cbToWrite);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006291
6292 if (!USE_VTP)
6293 {
6294 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cells,
6295 coord, &written);
6296 /* When writing fails or didn't write a single character, pretend one
6297 * character was written, otherwise we get stuck. */
6298 if (WriteConsoleOutputCharacterW(g_hConOut, unicodebuf, length,
6299 coord, &cchwritten) == 0
6300 || cchwritten == 0)
6301 cchwritten = 1;
6302 }
6303 else
6304 {
6305 if (WriteConsoleW(g_hConOut, unicodebuf, length, &cchwritten,
6306 NULL) == 0 || cchwritten == 0)
6307 cchwritten = 1;
6308 }
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006309
6310 if (cchwritten == length)
6311 {
6312 written = cbToWrite;
6313 g_coord.X += (SHORT)cells;
6314 }
6315 else
6316 {
6317 char_u *p = pchBuf;
6318 for (n = 0; n < cchwritten; n++)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01006319 MB_CPTR_ADV(p);
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006320 written = p - pchBuf;
6321 g_coord.X += (SHORT)mb_string2cells(pchBuf, written);
6322 }
6323 }
6324 else
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006325 {
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006326 if (!USE_VTP)
6327 {
6328 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cbToWrite,
6329 coord, &written);
6330 /* When writing fails or didn't write a single character, pretend one
6331 * character was written, otherwise we get stuck. */
6332 if (WriteConsoleOutputCharacter(g_hConOut, (LPCSTR)pchBuf, cbToWrite,
6333 coord, &written) == 0
6334 || written == 0)
6335 written = 1;
6336 }
6337 else
6338 {
6339 if (WriteConsole(g_hConOut, (LPCSTR)pchBuf, cbToWrite, &written,
6340 NULL) == 0 || written == 0)
6341 written = 1;
6342 }
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006343
6344 g_coord.X += (SHORT) written;
6345 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006346
6347 while (g_coord.X > g_srScrollRegion.Right)
6348 {
6349 g_coord.X -= (SHORT) Columns;
6350 if (g_coord.Y < g_srScrollRegion.Bottom)
6351 g_coord.Y++;
6352 }
6353
6354 gotoxy(g_coord.X + 1, g_coord.Y + 1);
6355
6356 return written;
6357}
6358
6359
6360/*
6361 * mch_write(): write the output buffer to the screen, translating ESC
6362 * sequences into calls to console output routines.
6363 */
6364 void
6365mch_write(
6366 char_u *s,
6367 int len)
6368{
6369 s[len] = NUL;
6370
6371 if (!term_console)
6372 {
6373 write(1, s, (unsigned)len);
6374 return;
6375 }
6376
6377 /* translate ESC | sequences into faked bios calls */
6378 while (len--)
6379 {
6380 /* optimization: use one single write_chars for runs of text,
6381 * rather than once per character It ain't curses, but it helps. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006382 DWORD prefix = (DWORD)strcspn((char *)s, "\n\r\b\a\033");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006383
6384 if (p_wd)
6385 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006386 WaitForChar(p_wd, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006387 if (prefix != 0)
6388 prefix = 1;
6389 }
6390
6391 if (prefix != 0)
6392 {
6393 DWORD nWritten;
6394
6395 nWritten = write_chars(s, prefix);
6396#ifdef MCH_WRITE_DUMP
6397 if (fdDump)
6398 {
6399 fputc('>', fdDump);
6400 fwrite(s, sizeof(char_u), nWritten, fdDump);
6401 fputs("<\n", fdDump);
6402 }
6403#endif
6404 len -= (nWritten - 1);
6405 s += nWritten;
6406 }
6407 else if (s[0] == '\n')
6408 {
6409 /* \n, newline: go to the beginning of the next line or scroll */
6410 if (g_coord.Y == g_srScrollRegion.Bottom)
6411 {
6412 scroll(1);
6413 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
6414 }
6415 else
6416 {
6417 gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
6418 }
6419#ifdef MCH_WRITE_DUMP
6420 if (fdDump)
6421 fputs("\\n\n", fdDump);
6422#endif
6423 s++;
6424 }
6425 else if (s[0] == '\r')
6426 {
6427 /* \r, carriage return: go to beginning of line */
6428 gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
6429#ifdef MCH_WRITE_DUMP
6430 if (fdDump)
6431 fputs("\\r\n", fdDump);
6432#endif
6433 s++;
6434 }
6435 else if (s[0] == '\b')
6436 {
6437 /* \b, backspace: move cursor one position left */
6438 if (g_coord.X > g_srScrollRegion.Left)
6439 g_coord.X--;
6440 else if (g_coord.Y > g_srScrollRegion.Top)
6441 {
6442 g_coord.X = g_srScrollRegion.Right;
6443 g_coord.Y--;
6444 }
6445 gotoxy(g_coord.X + 1, g_coord.Y + 1);
6446#ifdef MCH_WRITE_DUMP
6447 if (fdDump)
6448 fputs("\\b\n", fdDump);
6449#endif
6450 s++;
6451 }
6452 else if (s[0] == '\a')
6453 {
6454 /* \a, bell */
6455 MessageBeep(0xFFFFFFFF);
6456#ifdef MCH_WRITE_DUMP
6457 if (fdDump)
6458 fputs("\\a\n", fdDump);
6459#endif
6460 s++;
6461 }
6462 else if (s[0] == ESC && len >= 3-1 && s[1] == '|')
6463 {
6464#ifdef MCH_WRITE_DUMP
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006465 char_u *old_s = s;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006466#endif
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006467 char_u *p;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006468 int arg1 = 0, arg2 = 0, argc = 0, args[16];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006469
6470 switch (s[2])
6471 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006472 case '0': case '1': case '2': case '3': case '4':
6473 case '5': case '6': case '7': case '8': case '9':
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006474 p = s + 1;
6475 do
6476 {
6477 ++p;
6478 args[argc] = getdigits(&p);
6479 argc += (argc < 15) ? 1 : 0;
6480 if (p > s + len)
6481 break;
6482 } while (*p == ';');
6483
Bram Moolenaar071d4272004-06-13 20:20:40 +00006484 if (p > s + len)
6485 break;
6486
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006487 arg1 = args[0];
6488 arg2 = args[1];
6489 if (*p == 'm')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006490 {
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006491 if (argc == 1 && args[0] == 0)
6492 normvideo();
6493 else if (argc == 1)
6494 {
6495 if (USE_VTP)
6496 textcolor((WORD) arg1);
6497 else
6498 textattr((WORD) arg1);
6499 }
6500 else if (USE_VTP)
6501 vtp_sgr_bulks(argc, args);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006502 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006503 else if (argc == 2 && *p == 'H')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006504 {
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006505 gotoxy(arg2, arg1);
6506 }
6507 else if (argc == 2 && *p == 'r')
6508 {
6509 set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
6510 }
6511 else if (argc == 1 && *p == 'A')
6512 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006513 gotoxy(g_coord.X + 1,
6514 max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
6515 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006516 else if (argc == 1 && *p == 'b')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006517 {
6518 textbackground((WORD) arg1);
6519 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006520 else if (argc == 1 && *p == 'C')
6521 {
6522 gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
6523 g_coord.Y + 1);
6524 }
6525 else if (argc == 1 && *p == 'f')
6526 {
6527 textcolor((WORD) arg1);
6528 }
6529 else if (argc == 1 && *p == 'H')
6530 {
6531 gotoxy(1, arg1);
6532 }
6533 else if (argc == 1 && *p == 'L')
6534 {
6535 insert_lines(arg1);
6536 }
6537 else if (argc == 1 && *p == 'M')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006538 {
6539 delete_lines(arg1);
6540 }
6541
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00006542 len -= (int)(p - s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006543 s = p + 1;
6544 break;
6545
Bram Moolenaar071d4272004-06-13 20:20:40 +00006546 case 'A':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006547 gotoxy(g_coord.X + 1,
6548 max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
6549 goto got3;
6550
6551 case 'B':
6552 visual_bell();
6553 goto got3;
6554
6555 case 'C':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006556 gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
6557 g_coord.Y + 1);
6558 goto got3;
6559
6560 case 'E':
6561 termcap_mode_end();
6562 goto got3;
6563
6564 case 'F':
6565 standout();
6566 goto got3;
6567
6568 case 'f':
6569 standend();
6570 goto got3;
6571
6572 case 'H':
6573 gotoxy(1, 1);
6574 goto got3;
6575
6576 case 'j':
6577 clear_to_end_of_display();
6578 goto got3;
6579
6580 case 'J':
6581 clear_screen();
6582 goto got3;
6583
6584 case 'K':
6585 clear_to_end_of_line();
6586 goto got3;
6587
6588 case 'L':
6589 insert_lines(1);
6590 goto got3;
6591
6592 case 'M':
6593 delete_lines(1);
6594 goto got3;
6595
6596 case 'S':
6597 termcap_mode_start();
6598 goto got3;
6599
6600 case 'V':
6601 cursor_visible(TRUE);
6602 goto got3;
6603
6604 case 'v':
6605 cursor_visible(FALSE);
6606 goto got3;
6607
6608 got3:
6609 s += 3;
6610 len -= 2;
6611 }
6612
6613#ifdef MCH_WRITE_DUMP
6614 if (fdDump)
6615 {
6616 fputs("ESC | ", fdDump);
6617 fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
6618 fputc('\n', fdDump);
6619 }
6620#endif
6621 }
6622 else
6623 {
6624 /* Write a single character */
6625 DWORD nWritten;
6626
6627 nWritten = write_chars(s, 1);
6628#ifdef MCH_WRITE_DUMP
6629 if (fdDump)
6630 {
6631 fputc('>', fdDump);
6632 fwrite(s, sizeof(char_u), nWritten, fdDump);
6633 fputs("<\n", fdDump);
6634 }
6635#endif
6636
6637 len -= (nWritten - 1);
6638 s += nWritten;
6639 }
6640 }
6641
6642#ifdef MCH_WRITE_DUMP
6643 if (fdDump)
6644 fflush(fdDump);
6645#endif
6646}
6647
6648#endif /* FEAT_GUI_W32 */
6649
6650
6651/*
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01006652 * Delay for "msec" milliseconds.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006653 */
6654 void
6655mch_delay(
6656 long msec,
Bram Moolenaar1266d672017-02-01 13:43:36 +01006657 int ignoreinput UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006658{
6659#ifdef FEAT_GUI_W32
6660 Sleep((int)msec); /* never wait for input */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006661#else /* Console */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006662 if (ignoreinput)
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006663# ifdef FEAT_MZSCHEME
6664 if (mzthreads_allowed() && p_mzq > 0 && msec > p_mzq)
6665 {
6666 int towait = p_mzq;
6667
6668 /* if msec is large enough, wait by portions in p_mzq */
6669 while (msec > 0)
6670 {
6671 mzvim_check_threads();
6672 if (msec < towait)
6673 towait = msec;
6674 Sleep(towait);
6675 msec -= towait;
6676 }
6677 }
6678 else
6679# endif
6680 Sleep((int)msec);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006681 else
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006682 WaitForChar(msec, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006683#endif
6684}
6685
6686
6687/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01006688 * This version of remove is not scared by a readonly (backup) file.
6689 * This can also remove a symbolic link like Unix.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006690 * Return 0 for success, -1 for failure.
6691 */
6692 int
6693mch_remove(char_u *name)
6694{
Bram Moolenaar071d4272004-06-13 20:20:40 +00006695 WCHAR *wn = NULL;
6696 int n;
6697
Bram Moolenaar203258c2016-01-17 22:15:16 +01006698 /*
6699 * On Windows, deleting a directory's symbolic link is done by
6700 * RemoveDirectory(): mch_rmdir. It seems unnatural, but it is fact.
6701 */
6702 if (mch_isdir(name) && mch_is_symbolic_link(name))
6703 return mch_rmdir(name);
6704
Bram Moolenaar12b559e2013-06-12 22:41:37 +02006705 win32_setattrs(name, FILE_ATTRIBUTE_NORMAL);
6706
Bram Moolenaar071d4272004-06-13 20:20:40 +00006707 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6708 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006709 wn = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006710 if (wn != NULL)
6711 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006712 n = DeleteFileW(wn) ? 0 : -1;
6713 vim_free(wn);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006714 return n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006715 }
6716 }
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006717 return DeleteFile((LPCSTR)name) ? 0 : -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006718}
6719
6720
6721/*
Bram Moolenaarb9c31e72016-09-29 15:18:57 +02006722 * Check for an "interrupt signal": CTRL-break or CTRL-C.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006723 */
6724 void
Bram Moolenaarb9c31e72016-09-29 15:18:57 +02006725mch_breakcheck(int force)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006726{
6727#ifndef FEAT_GUI_W32 /* never used */
6728 if (g_fCtrlCPressed || g_fCBrkPressed)
6729 {
Bram Moolenaar9698ad72017-08-12 14:52:15 +02006730 ctrl_break_was_pressed = g_fCBrkPressed;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006731 g_fCtrlCPressed = g_fCBrkPressed = FALSE;
6732 got_int = TRUE;
6733 }
6734#endif
6735}
6736
Bram Moolenaaree273972016-01-02 21:11:51 +01006737/* physical RAM to leave for the OS */
6738#define WINNT_RESERVE_BYTES (256*1024*1024)
Bram Moolenaaree273972016-01-02 21:11:51 +01006739
6740/*
6741 * How much main memory in KiB that can be used by VIM.
6742 */
Bram Moolenaaree273972016-01-02 21:11:51 +01006743 long_u
Bram Moolenaar1266d672017-02-01 13:43:36 +01006744mch_total_mem(int special UNUSED)
Bram Moolenaaree273972016-01-02 21:11:51 +01006745{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006746 MEMORYSTATUSEX ms;
6747
Bram Moolenaaree273972016-01-02 21:11:51 +01006748 PlatformId();
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006749 /* Need to use GlobalMemoryStatusEx() when there is more memory than
6750 * what fits in 32 bits. But it's not always available. */
6751 ms.dwLength = sizeof(MEMORYSTATUSEX);
6752 GlobalMemoryStatusEx(&ms);
6753 if (ms.ullAvailVirtual < ms.ullTotalPhys)
Bram Moolenaaree273972016-01-02 21:11:51 +01006754 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006755 /* Process address space fits in physical RAM, use all of it. */
6756 return (long_u)(ms.ullAvailVirtual / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01006757 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006758 if (ms.ullTotalPhys <= WINNT_RESERVE_BYTES)
Bram Moolenaaree273972016-01-02 21:11:51 +01006759 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006760 /* Catch old NT box or perverse hardware setup. */
6761 return (long_u)((ms.ullTotalPhys / 2) / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01006762 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006763 /* Use physical RAM less reserve for OS + data. */
6764 return (long_u)((ms.ullTotalPhys - WINNT_RESERVE_BYTES) / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01006765}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006766
Bram Moolenaar071d4272004-06-13 20:20:40 +00006767/*
6768 * Same code as below, but with wide functions and no comments.
6769 * Return 0 for success, non-zero for failure.
6770 */
6771 int
6772mch_wrename(WCHAR *wold, WCHAR *wnew)
6773{
6774 WCHAR *p;
6775 int i;
6776 WCHAR szTempFile[_MAX_PATH + 1];
6777 WCHAR szNewPath[_MAX_PATH + 1];
6778 HANDLE hf;
6779
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006780 p = wold;
6781 for (i = 0; wold[i] != NUL; ++i)
6782 if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':')
6783 && wold[i + 1] != 0)
6784 p = wold + i + 1;
6785 if ((int)(wold + i - p) < 8 || p[6] != '~')
6786 return (MoveFileW(wold, wnew) == 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006787
6788 if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL)
6789 return -1;
6790 *p = NUL;
6791
6792 if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0)
6793 return -2;
6794
6795 if (!DeleteFileW(szTempFile))
6796 return -3;
6797
6798 if (!MoveFileW(wold, szTempFile))
6799 return -4;
6800
6801 if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW,
6802 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
6803 return -5;
6804 if (!CloseHandle(hf))
6805 return -6;
6806
6807 if (!MoveFileW(szTempFile, wnew))
6808 {
6809 (void)MoveFileW(szTempFile, wold);
6810 return -7;
6811 }
6812
6813 DeleteFileW(szTempFile);
6814
6815 if (!DeleteFileW(wold))
6816 return -8;
6817
6818 return 0;
6819}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006820
6821
6822/*
6823 * mch_rename() works around a bug in rename (aka MoveFile) in
6824 * Windows 95: rename("foo.bar", "foo.bar~") will generate a
6825 * file whose short file name is "FOO.BAR" (its long file name will
6826 * be correct: "foo.bar~"). Because a file can be accessed by
6827 * either its SFN or its LFN, "foo.bar" has effectively been
6828 * renamed to "foo.bar", which is not at all what was wanted. This
6829 * seems to happen only when renaming files with three-character
6830 * extensions by appending a suffix that does not include ".".
6831 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
6832 *
6833 * There is another problem, which isn't really a bug but isn't right either:
6834 * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be
6835 * "abcdef~1.txt" again. This has been reported on Windows NT 4.0 with
6836 * service pack 6. Doesn't seem to happen on Windows 98.
6837 *
6838 * Like rename(), returns 0 upon success, non-zero upon failure.
6839 * Should probably set errno appropriately when errors occur.
6840 */
6841 int
6842mch_rename(
6843 const char *pszOldFile,
6844 const char *pszNewFile)
6845{
6846 char szTempFile[_MAX_PATH+1];
6847 char szNewPath[_MAX_PATH+1];
6848 char *pszFilePart;
6849 HANDLE hf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006850 WCHAR *wold = NULL;
6851 WCHAR *wnew = NULL;
6852 int retval = -1;
6853
6854 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6855 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006856 wold = enc_to_utf16((char_u *)pszOldFile, NULL);
6857 wnew = enc_to_utf16((char_u *)pszNewFile, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006858 if (wold != NULL && wnew != NULL)
6859 retval = mch_wrename(wold, wnew);
6860 vim_free(wold);
6861 vim_free(wnew);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006862 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006863 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006864
6865 /*
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006866 * No need to play tricks unless the file name contains a "~" as the
6867 * seventh character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006868 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006869 pszFilePart = (char *)gettail((char_u *)pszOldFile);
6870 if (STRLEN(pszFilePart) < 8 || pszFilePart[6] != '~')
6871 return rename(pszOldFile, pszNewFile);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006872
6873 /* Get base path of new file name. Undocumented feature: If pszNewFile is
6874 * a directory, no error is returned and pszFilePart will be NULL. */
6875 if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0
6876 || pszFilePart == NULL)
6877 return -1;
6878 *pszFilePart = NUL;
6879
6880 /* Get (and create) a unique temporary file name in directory of new file */
6881 if (GetTempFileName(szNewPath, "VIM", 0, szTempFile) == 0)
6882 return -2;
6883
6884 /* blow the temp file away */
6885 if (!DeleteFile(szTempFile))
6886 return -3;
6887
6888 /* rename old file to the temp file */
6889 if (!MoveFile(pszOldFile, szTempFile))
6890 return -4;
6891
6892 /* now create an empty file called pszOldFile; this prevents the operating
6893 * system using pszOldFile as an alias (SFN) if we're renaming within the
6894 * same directory. For example, we're editing a file called
6895 * filename.asc.txt by its SFN, filena~1.txt. If we rename filena~1.txt
6896 * to filena~1.txt~ (i.e., we're making a backup while writing it), the
6897 * SFN for filena~1.txt~ will be filena~1.txt, by default, which will
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006898 * cause all sorts of problems later in buf_write(). So, we create an
6899 * empty file called filena~1.txt and the system will have to find some
6900 * other SFN for filena~1.txt~, such as filena~2.txt
Bram Moolenaar071d4272004-06-13 20:20:40 +00006901 */
6902 if ((hf = CreateFile(pszOldFile, GENERIC_WRITE, 0, NULL, CREATE_NEW,
6903 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
6904 return -5;
6905 if (!CloseHandle(hf))
6906 return -6;
6907
6908 /* rename the temp file to the new file */
6909 if (!MoveFile(szTempFile, pszNewFile))
6910 {
6911 /* Renaming failed. Rename the file back to its old name, so that it
6912 * looks like nothing happened. */
6913 (void)MoveFile(szTempFile, pszOldFile);
6914
6915 return -7;
6916 }
6917
6918 /* Seems to be left around on Novell filesystems */
6919 DeleteFile(szTempFile);
6920
6921 /* finally, remove the empty old file */
6922 if (!DeleteFile(pszOldFile))
6923 return -8;
6924
6925 return 0; /* success */
6926}
6927
6928/*
6929 * Get the default shell for the current hardware platform
6930 */
6931 char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006932default_shell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006933{
Bram Moolenaar071d4272004-06-13 20:20:40 +00006934 PlatformId();
6935
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006936 return "cmd.exe";
Bram Moolenaar071d4272004-06-13 20:20:40 +00006937}
6938
6939/*
6940 * mch_access() extends access() to do more detailed check on network drives.
6941 * Returns 0 if file "n" has access rights according to "p", -1 otherwise.
6942 */
6943 int
6944mch_access(char *n, int p)
6945{
6946 HANDLE hFile;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006947 int retval = -1; /* default: fail */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006948 WCHAR *wn = NULL;
6949
6950 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006951 wn = enc_to_utf16((char_u *)n, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006952
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006953 if (mch_isdir((char_u *)n))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006954 {
6955 char TempName[_MAX_PATH + 16] = "";
Bram Moolenaar071d4272004-06-13 20:20:40 +00006956 WCHAR TempNameW[_MAX_PATH + 16] = L"";
Bram Moolenaar071d4272004-06-13 20:20:40 +00006957
6958 if (p & R_OK)
6959 {
6960 /* Read check is performed by seeing if we can do a find file on
6961 * the directory for any file. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006962 if (wn != NULL)
6963 {
6964 int i;
6965 WIN32_FIND_DATAW d;
6966
6967 for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i)
6968 TempNameW[i] = wn[i];
6969 if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/')
6970 TempNameW[i++] = '\\';
6971 TempNameW[i++] = '*';
6972 TempNameW[i++] = 0;
6973
6974 hFile = FindFirstFileW(TempNameW, &d);
6975 if (hFile == INVALID_HANDLE_VALUE)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006976 goto getout;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006977 else
6978 (void)FindClose(hFile);
6979 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006980 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006981 {
6982 char *pch;
6983 WIN32_FIND_DATA d;
6984
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006985 vim_strncpy((char_u *)TempName, (char_u *)n, _MAX_PATH);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006986 pch = TempName + STRLEN(TempName) - 1;
6987 if (*pch != '\\' && *pch != '/')
6988 *++pch = '\\';
6989 *++pch = '*';
6990 *++pch = NUL;
6991
6992 hFile = FindFirstFile(TempName, &d);
6993 if (hFile == INVALID_HANDLE_VALUE)
6994 goto getout;
6995 (void)FindClose(hFile);
6996 }
6997 }
6998
6999 if (p & W_OK)
7000 {
7001 /* Trying to create a temporary file in the directory should catch
7002 * directories on read-only network shares. However, in
7003 * directories whose ACL allows writes but denies deletes will end
7004 * up keeping the temporary file :-(. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007005 if (wn != NULL)
7006 {
7007 if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW))
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007008 goto getout;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007009 else
7010 DeleteFileW(TempNameW);
7011 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007012 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007013 {
7014 if (!GetTempFileName(n, "VIM", 0, TempName))
7015 goto getout;
7016 mch_remove((char_u *)TempName);
7017 }
7018 }
7019 }
7020 else
7021 {
Bram Moolenaar5aa98962018-05-06 17:09:38 +02007022 // Don't consider a file read-only if another process has opened it.
7023 DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7024
Bram Moolenaar071d4272004-06-13 20:20:40 +00007025 /* Trying to open the file for the required access does ACL, read-only
7026 * network share, and file attribute checks. */
Bram Moolenaar5aa98962018-05-06 17:09:38 +02007027 DWORD access_mode = ((p & W_OK) ? GENERIC_WRITE : 0)
7028 | ((p & R_OK) ? GENERIC_READ : 0);
7029
Bram Moolenaar071d4272004-06-13 20:20:40 +00007030 if (wn != NULL)
Bram Moolenaar5aa98962018-05-06 17:09:38 +02007031 hFile = CreateFileW(wn, access_mode, share_mode,
7032 NULL, OPEN_EXISTING, 0, NULL);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007033 else
Bram Moolenaar5aa98962018-05-06 17:09:38 +02007034 hFile = CreateFile(n, access_mode, share_mode,
7035 NULL, OPEN_EXISTING, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007036 if (hFile == INVALID_HANDLE_VALUE)
7037 goto getout;
7038 CloseHandle(hFile);
7039 }
7040
7041 retval = 0; /* success */
7042getout:
Bram Moolenaar071d4272004-06-13 20:20:40 +00007043 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007044 return retval;
7045}
7046
Bram Moolenaar071d4272004-06-13 20:20:40 +00007047/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007048 * Version of open() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007049 */
7050 int
Bram Moolenaarb6843a02017-08-02 22:07:12 +02007051mch_open(const char *name, int flags, int mode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007052{
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00007053 /* _wopen() does not work with Borland C 5.5: creates a read-only file. */
Bram Moolenaara12a1612019-01-24 16:39:02 +01007054#ifndef __BORLANDC__
Bram Moolenaar071d4272004-06-13 20:20:40 +00007055 WCHAR *wn;
7056 int f;
7057
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00007058 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007059 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01007060 wn = enc_to_utf16((char_u *)name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007061 if (wn != NULL)
7062 {
7063 f = _wopen(wn, flags, mode);
7064 vim_free(wn);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007065 return f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007066 }
7067 }
Bram Moolenaara12a1612019-01-24 16:39:02 +01007068#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007069
Bram Moolenaarf9e6c3b2014-11-05 18:36:03 +01007070 /* open() can open a file which name is longer than _MAX_PATH bytes
7071 * and shorter than _MAX_PATH characters successfully, but sometimes it
7072 * causes unexpected error in another part. We make it an error explicitly
7073 * here. */
7074 if (strlen(name) >= _MAX_PATH)
7075 return -1;
7076
Bram Moolenaar071d4272004-06-13 20:20:40 +00007077 return open(name, flags, mode);
7078}
7079
7080/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007081 * Version of fopen() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007082 */
7083 FILE *
Bram Moolenaarb6843a02017-08-02 22:07:12 +02007084mch_fopen(const char *name, const char *mode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007085{
7086 WCHAR *wn, *wm;
7087 FILE *f = NULL;
7088
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007089 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007090 {
Bram Moolenaara12a1612019-01-24 16:39:02 +01007091#if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0fde2902008-03-16 13:54:13 +00007092 /* Work around an annoying assertion in the Microsoft debug CRT
7093 * when mode's text/binary setting doesn't match _get_fmode(). */
7094 char newMode = mode[strlen(mode) - 1];
7095 int oldMode = 0;
7096
7097 _get_fmode(&oldMode);
7098 if (newMode == 't')
7099 _set_fmode(_O_TEXT);
7100 else if (newMode == 'b')
7101 _set_fmode(_O_BINARY);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007102#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01007103 wn = enc_to_utf16((char_u *)name, NULL);
7104 wm = enc_to_utf16((char_u *)mode, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007105 if (wn != NULL && wm != NULL)
7106 f = _wfopen(wn, wm);
7107 vim_free(wn);
7108 vim_free(wm);
Bram Moolenaar0fde2902008-03-16 13:54:13 +00007109
Bram Moolenaara12a1612019-01-24 16:39:02 +01007110#if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0fde2902008-03-16 13:54:13 +00007111 _set_fmode(oldMode);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007112#endif
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007113 return f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007114 }
7115
Bram Moolenaarf9e6c3b2014-11-05 18:36:03 +01007116 /* fopen() can open a file which name is longer than _MAX_PATH bytes
7117 * and shorter than _MAX_PATH characters successfully, but sometimes it
7118 * causes unexpected error in another part. We make it an error explicitly
7119 * here. */
7120 if (strlen(name) >= _MAX_PATH)
7121 return NULL;
7122
Bram Moolenaar071d4272004-06-13 20:20:40 +00007123 return fopen(name, mode);
7124}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007125
Bram Moolenaar071d4272004-06-13 20:20:40 +00007126/*
7127 * SUB STREAM (aka info stream) handling:
7128 *
7129 * NTFS can have sub streams for each file. Normal contents of file is
7130 * stored in the main stream, and extra contents (author information and
7131 * title and so on) can be stored in sub stream. After Windows 2000, user
7132 * can access and store those informations in sub streams via explorer's
7133 * property menuitem in right click menu. Those informations in sub streams
7134 * were lost when copying only the main stream. So we have to copy sub
7135 * streams.
7136 *
7137 * Incomplete explanation:
7138 * http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp
7139 * More useful info and an example:
7140 * http://www.sysinternals.com/ntw2k/source/misc.shtml#streams
7141 */
7142
7143/*
7144 * Copy info stream data "substream". Read from the file with BackupRead(sh)
7145 * and write to stream "substream" of file "to".
7146 * Errors are ignored.
7147 */
7148 static void
7149copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len)
7150{
7151 HANDLE hTo;
7152 WCHAR *to_name;
7153
7154 to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR));
7155 wcscpy(to_name, to);
7156 wcscat(to_name, substream);
7157
7158 hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
7159 FILE_ATTRIBUTE_NORMAL, NULL);
7160 if (hTo != INVALID_HANDLE_VALUE)
7161 {
7162 long done;
7163 DWORD todo;
7164 DWORD readcnt, written;
7165 char buf[4096];
7166
7167 /* Copy block of bytes at a time. Abort when something goes wrong. */
7168 for (done = 0; done < len; done += written)
7169 {
7170 /* (size_t) cast for Borland C 5.5 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007171 todo = (DWORD)((size_t)(len - done) > sizeof(buf) ? sizeof(buf)
7172 : (size_t)(len - done));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007173 if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt,
7174 FALSE, FALSE, context)
7175 || readcnt != todo
7176 || !WriteFile(hTo, buf, todo, &written, NULL)
7177 || written != todo)
7178 break;
7179 }
7180 CloseHandle(hTo);
7181 }
7182
7183 free(to_name);
7184}
7185
7186/*
7187 * Copy info streams from file "from" to file "to".
7188 */
7189 static void
7190copy_infostreams(char_u *from, char_u *to)
7191{
7192 WCHAR *fromw;
7193 WCHAR *tow;
7194 HANDLE sh;
7195 WIN32_STREAM_ID sid;
7196 int headersize;
7197 WCHAR streamname[_MAX_PATH];
7198 DWORD readcount;
7199 void *context = NULL;
7200 DWORD lo, hi;
7201 int len;
7202
7203 /* Convert the file names to wide characters. */
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007204 fromw = enc_to_utf16(from, NULL);
7205 tow = enc_to_utf16(to, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007206 if (fromw != NULL && tow != NULL)
7207 {
7208 /* Open the file for reading. */
7209 sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL,
7210 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
7211 if (sh != INVALID_HANDLE_VALUE)
7212 {
7213 /* Use BackupRead() to find the info streams. Repeat until we
7214 * have done them all.*/
7215 for (;;)
7216 {
7217 /* Get the header to find the length of the stream name. If
7218 * the "readcount" is zero we have done all info streams. */
7219 ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007220 headersize = (int)((char *)&sid.cStreamName - (char *)&sid.dwStreamId);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007221 if (!BackupRead(sh, (LPBYTE)&sid, headersize,
7222 &readcount, FALSE, FALSE, &context)
7223 || readcount == 0)
7224 break;
7225
7226 /* We only deal with streams that have a name. The normal
7227 * file data appears to be without a name, even though docs
7228 * suggest it is called "::$DATA". */
7229 if (sid.dwStreamNameSize > 0)
7230 {
7231 /* Read the stream name. */
7232 if (!BackupRead(sh, (LPBYTE)streamname,
7233 sid.dwStreamNameSize,
7234 &readcount, FALSE, FALSE, &context))
7235 break;
7236
7237 /* Copy an info stream with a name ":anything:$DATA".
7238 * Skip "::$DATA", it has no stream name (examples suggest
7239 * it might be used for the normal file contents).
7240 * Note that BackupRead() counts bytes, but the name is in
7241 * wide characters. */
7242 len = readcount / sizeof(WCHAR);
7243 streamname[len] = 0;
7244 if (len > 7 && wcsicmp(streamname + len - 6,
7245 L":$DATA") == 0)
7246 {
7247 streamname[len - 6] = 0;
7248 copy_substream(sh, &context, tow, streamname,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00007249 (long)sid.Size.u.LowPart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007250 }
7251 }
7252
7253 /* Advance to the next stream. We might try seeking too far,
7254 * but BackupSeek() doesn't skip over stream borders, thus
7255 * that's OK. */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00007256 (void)BackupSeek(sh, sid.Size.u.LowPart, sid.Size.u.HighPart,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007257 &lo, &hi, &context);
7258 }
7259
7260 /* Clear the context. */
7261 (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context);
7262
7263 CloseHandle(sh);
7264 }
7265 }
7266 vim_free(fromw);
7267 vim_free(tow);
7268}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007269
7270/*
7271 * Copy file attributes from file "from" to file "to".
7272 * For Windows NT and later we copy info streams.
7273 * Always returns zero, errors are ignored.
7274 */
7275 int
7276mch_copy_file_attribute(char_u *from, char_u *to)
7277{
Bram Moolenaar071d4272004-06-13 20:20:40 +00007278 /* File streams only work on Windows NT and later. */
7279 PlatformId();
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007280 copy_infostreams(from, to);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007281 return 0;
7282}
7283
7284#if defined(MYRESETSTKOFLW) || defined(PROTO)
7285/*
7286 * Recreate a destroyed stack guard page in win32.
7287 * Written by Benjamin Peterson.
7288 */
7289
7290/* These magic numbers are from the MS header files */
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007291# define MIN_STACK_WINNT 2
Bram Moolenaar071d4272004-06-13 20:20:40 +00007292
7293/*
7294 * This function does the same thing as _resetstkoflw(), which is only
7295 * available in DevStudio .net and later.
7296 * Returns 0 for failure, 1 for success.
7297 */
7298 int
7299myresetstkoflw(void)
7300{
7301 BYTE *pStackPtr;
7302 BYTE *pGuardPage;
7303 BYTE *pStackBase;
7304 BYTE *pLowestPossiblePage;
7305 MEMORY_BASIC_INFORMATION mbi;
7306 SYSTEM_INFO si;
7307 DWORD nPageSize;
7308 DWORD dummy;
7309
Bram Moolenaar071d4272004-06-13 20:20:40 +00007310 PlatformId();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007311
7312 /* We need to know the system page size. */
7313 GetSystemInfo(&si);
7314 nPageSize = si.dwPageSize;
7315
7316 /* ...and the current stack pointer */
7317 pStackPtr = (BYTE*)_alloca(1);
7318
7319 /* ...and the base of the stack. */
7320 if (VirtualQuery(pStackPtr, &mbi, sizeof mbi) == 0)
7321 return 0;
7322 pStackBase = (BYTE*)mbi.AllocationBase;
7323
7324 /* ...and the page thats min_stack_req pages away from stack base; this is
7325 * the lowest page we could use. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007326 pLowestPossiblePage = pStackBase + MIN_STACK_WINNT * nPageSize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007327
Bram Moolenaar071d4272004-06-13 20:20:40 +00007328 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007329 /* We want the first committed page in the stack Start at the stack
7330 * base and move forward through memory until we find a committed block.
7331 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007332 BYTE *pBlock = pStackBase;
7333
Bram Moolenaara466c992005-07-09 21:03:22 +00007334 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007335 {
7336 if (VirtualQuery(pBlock, &mbi, sizeof mbi) == 0)
7337 return 0;
7338
7339 pBlock += mbi.RegionSize;
7340
7341 if (mbi.State & MEM_COMMIT)
7342 break;
7343 }
7344
7345 /* mbi now describes the first committed block in the stack. */
7346 if (mbi.Protect & PAGE_GUARD)
7347 return 1;
7348
7349 /* decide where the guard page should start */
7350 if ((long_u)(mbi.BaseAddress) < (long_u)pLowestPossiblePage)
7351 pGuardPage = pLowestPossiblePage;
7352 else
7353 pGuardPage = (BYTE*)mbi.BaseAddress;
7354
7355 /* allocate the guard page */
7356 if (!VirtualAlloc(pGuardPage, nPageSize, MEM_COMMIT, PAGE_READWRITE))
7357 return 0;
7358
7359 /* apply the guard attribute to the page */
7360 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_READWRITE | PAGE_GUARD,
7361 &dummy))
7362 return 0;
7363 }
7364
7365 return 1;
7366}
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007367#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007368
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007369
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007370/*
7371 * The command line arguments in UCS2
7372 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00007373static int nArgsW = 0;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007374static LPWSTR *ArglistW = NULL;
7375static int global_argc = 0;
7376static char **global_argv;
7377
7378static int used_file_argc = 0; /* last argument in global_argv[] used
7379 for the argument list. */
7380static int *used_file_indexes = NULL; /* indexes in global_argv[] for
7381 command line arguments added to
7382 the argument list */
7383static int used_file_count = 0; /* nr of entries in used_file_indexes */
7384static int used_file_literal = FALSE; /* take file names literally */
7385static int used_file_full_path = FALSE; /* file name was full path */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007386static int used_file_diff_mode = FALSE; /* file name was with diff mode */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007387static int used_alist_count = 0;
7388
7389
7390/*
7391 * Get the command line arguments. Unicode version.
7392 * Returns argc. Zero when something fails.
7393 */
7394 int
7395get_cmd_argsW(char ***argvp)
7396{
7397 char **argv = NULL;
7398 int argc = 0;
7399 int i;
7400
Bram Moolenaar14993322014-09-09 12:25:33 +02007401 free_cmd_argsW();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007402 ArglistW = CommandLineToArgvW(GetCommandLineW(), &nArgsW);
7403 if (ArglistW != NULL)
7404 {
7405 argv = malloc((nArgsW + 1) * sizeof(char *));
7406 if (argv != NULL)
7407 {
7408 argc = nArgsW;
7409 argv[argc] = NULL;
7410 for (i = 0; i < argc; ++i)
7411 {
7412 int len;
7413
7414 /* Convert each Unicode argument to the current codepage. */
7415 WideCharToMultiByte_alloc(GetACP(), 0,
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007416 ArglistW[i], (int)wcslen(ArglistW[i]) + 1,
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007417 (LPSTR *)&argv[i], &len, 0, 0);
7418 if (argv[i] == NULL)
7419 {
7420 /* Out of memory, clear everything. */
7421 while (i > 0)
7422 free(argv[--i]);
7423 free(argv);
Bram Moolenaar73c61632013-12-07 14:48:10 +01007424 argv = NULL;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007425 argc = 0;
7426 }
7427 }
7428 }
7429 }
7430
7431 global_argc = argc;
7432 global_argv = argv;
7433 if (argc > 0)
Bram Moolenaar14993322014-09-09 12:25:33 +02007434 {
7435 if (used_file_indexes != NULL)
7436 free(used_file_indexes);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007437 used_file_indexes = malloc(argc * sizeof(int));
Bram Moolenaar14993322014-09-09 12:25:33 +02007438 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007439
7440 if (argvp != NULL)
7441 *argvp = argv;
7442 return argc;
7443}
7444
7445 void
7446free_cmd_argsW(void)
7447{
7448 if (ArglistW != NULL)
7449 {
7450 GlobalFree(ArglistW);
7451 ArglistW = NULL;
7452 }
7453}
7454
7455/*
7456 * Remember "name" is an argument that was added to the argument list.
7457 * This avoids that we have to re-parse the argument list when fix_arg_enc()
7458 * is called.
7459 */
7460 void
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007461used_file_arg(char *name, int literal, int full_path, int diff_mode)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007462{
7463 int i;
7464
7465 if (used_file_indexes == NULL)
7466 return;
7467 for (i = used_file_argc + 1; i < global_argc; ++i)
7468 if (STRCMP(global_argv[i], name) == 0)
7469 {
7470 used_file_argc = i;
7471 used_file_indexes[used_file_count++] = i;
7472 break;
7473 }
7474 used_file_literal = literal;
7475 used_file_full_path = full_path;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007476 used_file_diff_mode = diff_mode;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007477}
7478
7479/*
7480 * Remember the length of the argument list as it was. If it changes then we
7481 * leave it alone when 'encoding' is set.
7482 */
7483 void
7484set_alist_count(void)
7485{
7486 used_alist_count = GARGCOUNT;
7487}
7488
7489/*
7490 * Fix the encoding of the command line arguments. Invoked when 'encoding'
7491 * has been changed while starting up. Use the UCS-2 command line arguments
7492 * and convert them to 'encoding'.
7493 */
7494 void
7495fix_arg_enc(void)
7496{
7497 int i;
7498 int idx;
7499 char_u *str;
Bram Moolenaar86b68352004-12-27 21:59:20 +00007500 int *fnum_list;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007501
7502 /* Safety checks:
7503 * - if argument count differs between the wide and non-wide argument
7504 * list, something must be wrong.
7505 * - the file name arguments must have been located.
7506 * - the length of the argument list wasn't changed by the user.
7507 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00007508 if (global_argc != nArgsW
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007509 || ArglistW == NULL
7510 || used_file_indexes == NULL
7511 || used_file_count == 0
7512 || used_alist_count != GARGCOUNT)
7513 return;
7514
Bram Moolenaar86b68352004-12-27 21:59:20 +00007515 /* Remember the buffer numbers for the arguments. */
7516 fnum_list = (int *)alloc((int)sizeof(int) * GARGCOUNT);
7517 if (fnum_list == NULL)
7518 return; /* out of memory */
7519 for (i = 0; i < GARGCOUNT; ++i)
7520 fnum_list[i] = GARGLIST[i].ae_fnum;
7521
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007522 /* Clear the argument list. Make room for the new arguments. */
7523 alist_clear(&global_alist);
7524 if (ga_grow(&global_alist.al_ga, used_file_count) == FAIL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00007525 return; /* out of memory */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007526
7527 for (i = 0; i < used_file_count; ++i)
7528 {
7529 idx = used_file_indexes[i];
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007530 str = utf16_to_enc(ArglistW[idx], NULL);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007531 if (str != NULL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00007532 {
Bram Moolenaar39d21e32017-08-05 23:09:31 +02007533 int literal = used_file_literal;
7534
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007535# ifdef FEAT_DIFF
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007536 /* When using diff mode may need to concatenate file name to
7537 * directory name. Just like it's done in main(). */
7538 if (used_file_diff_mode && mch_isdir(str) && GARGCOUNT > 0
7539 && !mch_isdir(alist_name(&GARGLIST[0])))
7540 {
7541 char_u *r;
7542
7543 r = concat_fnames(str, gettail(alist_name(&GARGLIST[0])), TRUE);
7544 if (r != NULL)
7545 {
7546 vim_free(str);
7547 str = r;
7548 }
7549 }
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007550# endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00007551 /* Re-use the old buffer by renaming it. When not using literal
7552 * names it's done by alist_expand() below. */
7553 if (used_file_literal)
7554 buf_set_name(fnum_list[i], str);
7555
Bram Moolenaar39d21e32017-08-05 23:09:31 +02007556 /* Check backtick literal. backtick literal is already expanded in
7557 * main.c, so this part add str as literal. */
7558 if (literal == FALSE)
7559 {
Bram Moolenaar116a0f82017-08-07 21:17:57 +02007560 size_t len = STRLEN(str);
7561
Bram Moolenaar39d21e32017-08-05 23:09:31 +02007562 if (len > 2 && *str == '`' && *(str + len - 1) == '`')
7563 literal = TRUE;
7564 }
7565 alist_add(&global_alist, str, literal ? 2 : 0);
Bram Moolenaar86b68352004-12-27 21:59:20 +00007566 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007567 }
7568
7569 if (!used_file_literal)
7570 {
7571 /* Now expand wildcards in the arguments. */
7572 /* Temporarily add '(' and ')' to 'isfname'. These are valid
7573 * filename characters but are excluded from 'isfname' to make
Bram Moolenaar20586cb2018-03-08 22:03:14 +01007574 * "gf" work on a file name in parenthesis (e.g.: see vim.h).
7575 * Also, unset wildignore to not be influenced by this option.
7576 * The arguments specified in command-line should be kept even if
7577 * encoding options were changed. */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007578 do_cmdline_cmd((char_u *)":let SaVe_ISF = &isf|set isf+=(,)");
Bram Moolenaar20586cb2018-03-08 22:03:14 +01007579 do_cmdline_cmd((char_u *)":let SaVe_WIG = &wig|set wig=");
Bram Moolenaar86b68352004-12-27 21:59:20 +00007580 alist_expand(fnum_list, used_alist_count);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007581 do_cmdline_cmd((char_u *)":let &isf = SaVe_ISF|unlet SaVe_ISF");
Bram Moolenaar20586cb2018-03-08 22:03:14 +01007582 do_cmdline_cmd((char_u *)":let &wig = SaVe_WIG|unlet SaVe_WIG");
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007583 }
7584
7585 /* If wildcard expansion failed, we are editing the first file of the
7586 * arglist and there is no file name: Edit the first argument now. */
7587 if (curwin->w_arg_idx == 0 && curbuf->b_fname == NULL)
7588 {
7589 do_cmdline_cmd((char_u *)":rewind");
7590 if (GARGCOUNT == 1 && used_file_full_path)
Bram Moolenaarb7407d32018-02-03 17:36:27 +01007591 (void)vim_chdirfile(alist_name(&GARGLIST[0]), "drop");
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007592 }
Bram Moolenaar86b68352004-12-27 21:59:20 +00007593
7594 set_alist_count();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007595}
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007596
7597 int
7598mch_setenv(char *var, char *value, int x)
7599{
7600 char_u *envbuf;
7601
7602 envbuf = alloc((unsigned)(STRLEN(var) + STRLEN(value) + 2));
7603 if (envbuf == NULL)
7604 return -1;
7605
7606 sprintf((char *)envbuf, "%s=%s", var, value);
7607
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007608 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
7609 {
7610 WCHAR *p = enc_to_utf16(envbuf, NULL);
7611
7612 vim_free(envbuf);
7613 if (p == NULL)
7614 return -1;
7615 _wputenv(p);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007616#ifdef libintl_wputenv
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007617 libintl_wputenv(p);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007618#endif
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007619 /* Unlike Un*x systems, we can free the string for _wputenv(). */
7620 vim_free(p);
7621 }
7622 else
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007623 {
7624 _putenv((char *)envbuf);
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007625#ifdef libintl_putenv
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007626 libintl_putenv((char *)envbuf);
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007627#endif
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007628 /* Unlike Un*x systems, we can free the string for _putenv(). */
7629 vim_free(envbuf);
7630 }
7631
7632 return 0;
7633}
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007634
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007635/*
7636 * Support for 256 colors and 24-bit colors was added in Windows 10
7637 * version 1703 (Creators update).
7638 */
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007639#define VTP_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 15063)
7640
7641/*
7642 * Support for pseudo-console (ConPTY) was added in windows 10
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01007643 * version 1809 (October 2018 update). However, that version is unstable.
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007644 */
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01007645#define CONPTY_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 17763)
7646#define CONPTY_STABLE_BUILD MAKE_VER(10, 0, 32767) // T.B.D.
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007647
7648 static void
7649vtp_flag_init(void)
7650{
7651 DWORD ver = get_build_number();
7652#ifndef FEAT_GUI_W32
7653 DWORD mode;
7654 HANDLE out;
7655
7656 out = GetStdHandle(STD_OUTPUT_HANDLE);
7657
7658 vtp_working = (ver >= VTP_FIRST_SUPPORT_BUILD) ? 1 : 0;
7659 GetConsoleMode(out, &mode);
7660 mode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
7661 if (SetConsoleMode(out, mode) == 0)
7662 vtp_working = 0;
7663#endif
7664
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007665 if (ver >= CONPTY_FIRST_SUPPORT_BUILD)
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01007666 conpty_working = 1;
7667 if (ver >= CONPTY_STABLE_BUILD)
7668 conpty_stable = 1;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007669
7670}
7671
7672#ifndef FEAT_GUI_W32
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007673
7674 static void
7675vtp_init(void)
7676{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007677 HMODULE hKerneldll;
7678 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi;
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007679# ifdef FEAT_TERMGUICOLORS
7680 COLORREF fg, bg;
7681# endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007682
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007683 /* Use functions supported from Vista */
7684 hKerneldll = GetModuleHandle("kernel32.dll");
7685 if (hKerneldll != NULL)
7686 {
7687 pGetConsoleScreenBufferInfoEx =
7688 (PfnGetConsoleScreenBufferInfoEx)GetProcAddress(
7689 hKerneldll, "GetConsoleScreenBufferInfoEx");
7690 pSetConsoleScreenBufferInfoEx =
7691 (PfnSetConsoleScreenBufferInfoEx)GetProcAddress(
7692 hKerneldll, "SetConsoleScreenBufferInfoEx");
7693 if (pGetConsoleScreenBufferInfoEx != NULL
7694 && pSetConsoleScreenBufferInfoEx != NULL)
7695 has_csbiex = TRUE;
7696 }
7697
7698 csbi.cbSize = sizeof(csbi);
7699 if (has_csbiex)
7700 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007701 save_console_bg_rgb = (guicolor_T)csbi.ColorTable[g_color_index_bg];
7702 save_console_fg_rgb = (guicolor_T)csbi.ColorTable[g_color_index_fg];
7703
7704# ifdef FEAT_TERMGUICOLORS
7705 bg = (COLORREF)csbi.ColorTable[g_color_index_bg];
7706 fg = (COLORREF)csbi.ColorTable[g_color_index_fg];
7707 bg = (GetRValue(bg) << 16) | (GetGValue(bg) << 8) | GetBValue(bg);
7708 fg = (GetRValue(fg) << 16) | (GetGValue(fg) << 8) | GetBValue(fg);
7709 default_console_color_bg = bg;
7710 default_console_color_fg = fg;
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007711# endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007712
7713 set_console_color_rgb();
7714}
7715
7716 static void
7717vtp_exit(void)
7718{
7719 reset_console_color_rgb();
7720}
7721
7722 static int
7723vtp_printf(
7724 char *format,
7725 ...)
7726{
7727 char_u buf[100];
7728 va_list list;
7729 DWORD result;
7730
7731 va_start(list, format);
7732 vim_vsnprintf((char *)buf, 100, (char *)format, list);
7733 va_end(list);
7734 WriteConsoleA(g_hConOut, buf, (DWORD)STRLEN(buf), &result, NULL);
7735 return (int)result;
7736}
7737
7738 static void
7739vtp_sgr_bulk(
7740 int arg)
7741{
7742 int args[1];
7743
7744 args[0] = arg;
7745 vtp_sgr_bulks(1, args);
7746}
7747
7748 static void
7749vtp_sgr_bulks(
7750 int argc,
7751 int *args
7752)
7753{
7754 /* 2('\033[') + 4('255.') * 16 + NUL */
7755 char_u buf[2 + (4 * 16) + 1];
7756 char_u *p;
7757 int i;
7758
7759 p = buf;
7760 *p++ = '\033';
7761 *p++ = '[';
7762
7763 for (i = 0; i < argc; ++i)
7764 {
7765 p += vim_snprintf((char *)p, 4, "%d", args[i] & 0xff);
7766 *p++ = ';';
7767 }
7768 p--;
7769 *p++ = 'm';
7770 *p = NUL;
7771 vtp_printf((char *)buf);
7772}
7773
Bram Moolenaar8a938af2018-05-01 17:30:41 +02007774# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007775 static int
7776ctermtoxterm(
7777 int cterm)
7778{
Bram Moolenaar9894e392018-05-05 14:29:06 +02007779 char_u r, g, b, idx;
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007780
7781 cterm_color2rgb(cterm, &r, &g, &b, &idx);
7782 return (((int)r << 16) | ((int)g << 8) | (int)b);
7783}
Bram Moolenaar8a938af2018-05-01 17:30:41 +02007784# endif
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007785
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007786 static void
7787set_console_color_rgb(void)
7788{
7789# ifdef FEAT_TERMGUICOLORS
7790 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi;
7791 int id;
7792 guicolor_T fg = INVALCOLOR;
7793 guicolor_T bg = INVALCOLOR;
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007794 int ctermfg;
7795 int ctermbg;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007796
7797 if (!USE_VTP)
7798 return;
7799
7800 id = syn_name2id((char_u *)"Normal");
7801 if (id > 0)
7802 syn_id2colors(id, &fg, &bg);
7803 if (fg == INVALCOLOR)
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007804 {
7805 ctermfg = -1;
7806 if (id > 0)
7807 syn_id2cterm_bg(id, &ctermfg, &ctermbg);
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007808 fg = ctermfg != -1 ? ctermtoxterm(ctermfg) : default_console_color_fg;
7809 cterm_normal_fg_gui_color = fg;
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007810 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007811 if (bg == INVALCOLOR)
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007812 {
7813 ctermbg = -1;
7814 if (id > 0)
7815 syn_id2cterm_bg(id, &ctermfg, &ctermbg);
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007816 bg = ctermbg != -1 ? ctermtoxterm(ctermbg) : default_console_color_bg;
7817 cterm_normal_bg_gui_color = bg;
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007818 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007819 fg = (GetRValue(fg) << 16) | (GetGValue(fg) << 8) | GetBValue(fg);
7820 bg = (GetRValue(bg) << 16) | (GetGValue(bg) << 8) | GetBValue(bg);
7821
7822 csbi.cbSize = sizeof(csbi);
7823 if (has_csbiex)
7824 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7825
7826 csbi.cbSize = sizeof(csbi);
7827 csbi.srWindow.Right += 1;
7828 csbi.srWindow.Bottom += 1;
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007829 csbi.ColorTable[g_color_index_bg] = (COLORREF)bg;
7830 csbi.ColorTable[g_color_index_fg] = (COLORREF)fg;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007831 if (has_csbiex)
7832 pSetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7833# endif
7834}
7835
7836 static void
7837reset_console_color_rgb(void)
7838{
7839# ifdef FEAT_TERMGUICOLORS
7840 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi;
7841
7842 csbi.cbSize = sizeof(csbi);
7843 if (has_csbiex)
7844 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7845
7846 csbi.cbSize = sizeof(csbi);
7847 csbi.srWindow.Right += 1;
7848 csbi.srWindow.Bottom += 1;
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007849 csbi.ColorTable[g_color_index_bg] = (COLORREF)save_console_bg_rgb;
7850 csbi.ColorTable[g_color_index_fg] = (COLORREF)save_console_fg_rgb;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007851 if (has_csbiex)
7852 pSetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7853# endif
7854}
7855
7856 void
7857control_console_color_rgb(void)
7858{
7859 if (USE_VTP)
7860 set_console_color_rgb();
7861 else
7862 reset_console_color_rgb();
7863}
7864
7865 int
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007866use_vtp(void)
7867{
7868 return USE_VTP;
7869}
7870
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007871 int
7872is_term_win32(void)
7873{
7874 return T_NAME != NULL && STRCMP(T_NAME, "win32") == 0;
7875}
7876
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007877 int
7878has_vtp_working(void)
7879{
7880 return vtp_working;
7881}
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01007882
Bram Moolenaar6902c0e2019-02-16 14:07:37 +01007883#endif
7884
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01007885 int
7886has_conpty_working(void)
7887{
7888 return conpty_working;
7889}
7890
7891 int
7892is_conpty_stable(void)
7893{
7894 return conpty_stable;
7895}