blob: 10ca41881e085b596a575cf9e229ae5ce0f0d05a [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 Moolenaarcafafb32018-02-22 21:07:09 +0100189static int vtp_working = 0;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +0100190static void vtp_flag_init();
191
192#ifndef FEAT_GUI_W32
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100193static void vtp_init();
194static void vtp_exit();
195static int vtp_printf(char *format, ...);
196static void vtp_sgr_bulk(int arg);
197static void vtp_sgr_bulks(int argc, int *argv);
198
199static guicolor_T save_console_bg_rgb;
200static guicolor_T save_console_fg_rgb;
201
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +0200202static int g_color_index_bg = 0;
203static int g_color_index_fg = 7;
204
205# ifdef FEAT_TERMGUICOLORS
206static int default_console_color_bg = 0x000000; // black
207static int default_console_color_fg = 0xc0c0c0; // white
208# endif
209
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100210# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarc5cd8852018-05-01 15:47:38 +0200211# define USE_VTP (vtp_working && is_term_win32() && (p_tgc || (!p_tgc && t_colors >= 256)))
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100212# else
213# define USE_VTP 0
214# endif
215
216static void set_console_color_rgb(void);
217static void reset_console_color_rgb(void);
218#endif
219
220/* This flag is newly created from Windows 10 */
221#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
222# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
223#endif
224
225#ifndef FEAT_GUI_W32
Bram Moolenaar071d4272004-06-13 20:20:40 +0000226static int suppress_winsize = 1; /* don't fiddle with console */
227#endif
228
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200229static char_u *exe_path = NULL;
230
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100231static BOOL win8_or_later = FALSE;
232
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100233#ifndef FEAT_GUI_W32
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100234/* Dynamic loading for portability */
235typedef struct _DYN_CONSOLE_SCREEN_BUFFER_INFOEX
236{
237 ULONG cbSize;
238 COORD dwSize;
239 COORD dwCursorPosition;
240 WORD wAttributes;
241 SMALL_RECT srWindow;
242 COORD dwMaximumWindowSize;
243 WORD wPopupAttributes;
244 BOOL bFullscreenSupported;
245 COLORREF ColorTable[16];
246} DYN_CONSOLE_SCREEN_BUFFER_INFOEX, *PDYN_CONSOLE_SCREEN_BUFFER_INFOEX;
247typedef BOOL (WINAPI *PfnGetConsoleScreenBufferInfoEx)(HANDLE, PDYN_CONSOLE_SCREEN_BUFFER_INFOEX);
248static PfnGetConsoleScreenBufferInfoEx pGetConsoleScreenBufferInfoEx;
249typedef BOOL (WINAPI *PfnSetConsoleScreenBufferInfoEx)(HANDLE, PDYN_CONSOLE_SCREEN_BUFFER_INFOEX);
250static PfnSetConsoleScreenBufferInfoEx pSetConsoleScreenBufferInfoEx;
251static BOOL has_csbiex = FALSE;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +0100252#endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100253
254/*
255 * Get version number including build number
256 */
257typedef BOOL (WINAPI *PfnRtlGetVersion)(LPOSVERSIONINFOW);
258# define MAKE_VER(major, minor, build) \
259 (((major) << 24) | ((minor) << 16) | (build))
260
261 static DWORD
262get_build_number(void)
263{
264 OSVERSIONINFOW osver = {sizeof(OSVERSIONINFOW)};
265 HMODULE hNtdll;
266 PfnRtlGetVersion pRtlGetVersion;
267 DWORD ver = MAKE_VER(0, 0, 0);
268
269 hNtdll = GetModuleHandle("ntdll.dll");
270 if (hNtdll != NULL)
271 {
272 pRtlGetVersion =
273 (PfnRtlGetVersion)GetProcAddress(hNtdll, "RtlGetVersion");
274 pRtlGetVersion(&osver);
275 ver = MAKE_VER(min(osver.dwMajorVersion, 255),
276 min(osver.dwMinorVersion, 255),
277 min(osver.dwBuildNumber, 32767));
278 }
279 return ver;
280}
281
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +0100282#ifndef FEAT_GUI_W32
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100283/*
284 * Version of ReadConsoleInput() that works with IME.
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100285 * Works around problems on Windows 8.
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100286 */
287 static BOOL
288read_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100289 HANDLE hInput,
290 INPUT_RECORD *lpBuffer,
291 DWORD nLength,
292 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100293{
294 enum
295 {
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100296 IRSIZE = 10
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100297 };
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100298 static INPUT_RECORD s_irCache[IRSIZE];
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100299 static DWORD s_dwIndex = 0;
300 static DWORD s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100301 DWORD dwEvents;
Bram Moolenaardd415a62014-02-05 14:02:27 +0100302 int head;
303 int tail;
304 int i;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100305
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200306 if (nLength == -2)
307 return (s_dwMax > 0) ? TRUE : FALSE;
308
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100309 if (!win8_or_later)
310 {
311 if (nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200312 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
313 return ReadConsoleInputW(hInput, lpBuffer, 1, &dwEvents);
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100314 }
315
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100316 if (s_dwMax == 0)
317 {
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100318 if (nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200319 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
320 if (!ReadConsoleInputW(hInput, s_irCache, IRSIZE, &dwEvents))
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100321 return FALSE;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100322 s_dwIndex = 0;
323 s_dwMax = dwEvents;
324 if (dwEvents == 0)
325 {
326 *lpEvents = 0;
327 return TRUE;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100328 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100329
330 if (s_dwMax > 1)
331 {
332 head = 0;
333 tail = s_dwMax - 1;
334 while (head != tail)
335 {
336 if (s_irCache[head].EventType == WINDOW_BUFFER_SIZE_EVENT
337 && s_irCache[head + 1].EventType
338 == WINDOW_BUFFER_SIZE_EVENT)
339 {
340 /* Remove duplicate event to avoid flicker. */
341 for (i = head; i < tail; ++i)
342 s_irCache[i] = s_irCache[i + 1];
343 --tail;
344 continue;
345 }
346 head++;
347 }
348 s_dwMax = tail + 1;
349 }
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100350 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100351
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100352 *lpBuffer = s_irCache[s_dwIndex];
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200353 if (!(nLength == -1 || nLength == -2) && ++s_dwIndex >= s_dwMax)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100354 s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100355 *lpEvents = 1;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100356 return TRUE;
357}
358
359/*
360 * Version of PeekConsoleInput() that works with IME.
361 */
362 static BOOL
363peek_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100364 HANDLE hInput,
365 INPUT_RECORD *lpBuffer,
366 DWORD nLength,
367 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100368{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100369 return read_console_input(hInput, lpBuffer, -1, lpEvents);
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100370}
371
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100372# ifdef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200373 static DWORD
374msg_wait_for_multiple_objects(
375 DWORD nCount,
376 LPHANDLE pHandles,
377 BOOL fWaitAll,
378 DWORD dwMilliseconds,
379 DWORD dwWakeMask)
380{
381 if (read_console_input(NULL, NULL, -2, NULL))
382 return WAIT_OBJECT_0;
383 return MsgWaitForMultipleObjects(nCount, pHandles, fWaitAll,
384 dwMilliseconds, dwWakeMask);
385}
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100386# endif
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200387
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100388# ifndef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200389 static DWORD
390wait_for_single_object(
391 HANDLE hHandle,
392 DWORD dwMilliseconds)
393{
394 if (read_console_input(NULL, NULL, -2, NULL))
395 return WAIT_OBJECT_0;
396 return WaitForSingleObject(hHandle, dwMilliseconds);
397}
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100398# endif
399#endif
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200400
Bram Moolenaar071d4272004-06-13 20:20:40 +0000401 static void
402get_exe_name(void)
403{
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100404 /* Maximum length of $PATH is more than MAXPATHL. 8191 is often mentioned
405 * as the maximum length that works (plus a NUL byte). */
406#define MAX_ENV_PATH_LEN 8192
407 char temp[MAX_ENV_PATH_LEN];
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200408 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000409
410 if (exe_name == NULL)
411 {
412 /* store the name of the executable, may be used for $VIM */
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100413 GetModuleFileName(NULL, temp, MAX_ENV_PATH_LEN - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000414 if (*temp != NUL)
415 exe_name = FullName_save((char_u *)temp, FALSE);
416 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000417
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200418 if (exe_path == NULL && exe_name != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000419 {
Bram Moolenaar6b5ef062010-10-27 12:18:00 +0200420 exe_path = vim_strnsave(exe_name,
421 (int)(gettail_sep(exe_name) - exe_name));
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200422 if (exe_path != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000423 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200424 /* Append our starting directory to $PATH, so that when doing
425 * "!xxd" it's found in our starting directory. Needed because
426 * SearchPath() also looks there. */
427 p = mch_getenv("PATH");
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100428 if (p == NULL
429 || STRLEN(p) + STRLEN(exe_path) + 2 < MAX_ENV_PATH_LEN)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200430 {
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100431 if (p == NULL || *p == NUL)
432 temp[0] = NUL;
433 else
434 {
435 STRCPY(temp, p);
436 STRCAT(temp, ";");
437 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200438 STRCAT(temp, exe_path);
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100439 vim_setenv((char_u *)"PATH", (char_u *)temp);
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200440 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000441 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000442 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000443}
444
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200445/*
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100446 * Unescape characters in "p" that appear in "escaped".
447 */
448 static void
449unescape_shellxquote(char_u *p, char_u *escaped)
450{
Bram Moolenaar4336cdf2012-02-29 13:58:47 +0100451 int l = (int)STRLEN(p);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100452 int n;
453
454 while (*p != NUL)
455 {
456 if (*p == '^' && vim_strchr(escaped, p[1]) != NULL)
457 mch_memmove(p, p + 1, l--);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100458 n = (*mb_ptr2len)(p);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100459 p += n;
460 l -= n;
461 }
462}
463
464/*
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200465 * Load library "name".
466 */
467 HINSTANCE
468vimLoadLib(char *name)
469{
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200470 HINSTANCE dll = NULL;
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200471
Bram Moolenaarfaca8402012-10-21 02:37:10 +0200472 /* NOTE: Do not use mch_dirname() and mch_chdir() here, they may call
473 * vimLoadLib() recursively, which causes a stack overflow. */
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200474 if (exe_path == NULL)
475 get_exe_name();
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200476 if (exe_path != NULL)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200477 {
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200478 WCHAR old_dirw[MAXPATHL];
479
480 if (GetCurrentDirectoryW(MAXPATHL, old_dirw) != 0)
481 {
482 /* Change directory to where the executable is, both to make
483 * sure we find a .dll there and to avoid looking for a .dll
484 * in the current directory. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100485 SetCurrentDirectory((LPCSTR)exe_path);
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200486 dll = LoadLibrary(name);
487 SetCurrentDirectoryW(old_dirw);
488 return dll;
489 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200490 }
491 return dll;
492}
493
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100494#if defined(DYNAMIC_ICONV) || defined(DYNAMIC_GETTEXT) || defined(PROTO)
495/*
496 * Get related information about 'funcname' which is imported by 'hInst'.
497 * If 'info' is 0, return the function address.
498 * If 'info' is 1, return the module name which the function is imported from.
499 */
500 static void *
501get_imported_func_info(HINSTANCE hInst, const char *funcname, int info)
502{
503 PBYTE pImage = (PBYTE)hInst;
504 PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)hInst;
505 PIMAGE_NT_HEADERS pPE;
506 PIMAGE_IMPORT_DESCRIPTOR pImpDesc;
507 PIMAGE_THUNK_DATA pIAT; /* Import Address Table */
508 PIMAGE_THUNK_DATA pINT; /* Import Name Table */
509 PIMAGE_IMPORT_BY_NAME pImpName;
510
511 if (pDOS->e_magic != IMAGE_DOS_SIGNATURE)
512 return NULL;
513 pPE = (PIMAGE_NT_HEADERS)(pImage + pDOS->e_lfanew);
514 if (pPE->Signature != IMAGE_NT_SIGNATURE)
515 return NULL;
516 pImpDesc = (PIMAGE_IMPORT_DESCRIPTOR)(pImage
517 + pPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
518 .VirtualAddress);
519 for (; pImpDesc->FirstThunk; ++pImpDesc)
520 {
521 if (!pImpDesc->OriginalFirstThunk)
522 continue;
523 pIAT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->FirstThunk);
524 pINT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->OriginalFirstThunk);
525 for (; pIAT->u1.Function; ++pIAT, ++pINT)
526 {
527 if (IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal))
528 continue;
529 pImpName = (PIMAGE_IMPORT_BY_NAME)(pImage
530 + (UINT_PTR)(pINT->u1.AddressOfData));
531 if (strcmp((char *)pImpName->Name, funcname) == 0)
532 {
533 switch (info)
534 {
535 case 0:
536 return (void *)pIAT->u1.Function;
537 case 1:
538 return (void *)(pImage + pImpDesc->Name);
539 default:
540 return NULL;
541 }
542 }
543 }
544 }
545 return NULL;
546}
547
548/*
549 * Get the module handle which 'funcname' in 'hInst' is imported from.
550 */
551 HINSTANCE
552find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname)
553{
554 char *modulename;
555
556 modulename = (char *)get_imported_func_info(hInst, funcname, 1);
557 if (modulename != NULL)
558 return GetModuleHandleA(modulename);
559 return NULL;
560}
561
562/*
563 * Get the address of 'funcname' which is imported by 'hInst' DLL.
564 */
565 void *
566get_dll_import_func(HINSTANCE hInst, const char *funcname)
567{
568 return get_imported_func_info(hInst, funcname, 0);
569}
570#endif
571
Bram Moolenaar071d4272004-06-13 20:20:40 +0000572#if defined(DYNAMIC_GETTEXT) || defined(PROTO)
573# ifndef GETTEXT_DLL
574# define GETTEXT_DLL "libintl.dll"
Bram Moolenaar7554c542018-10-06 15:03:15 +0200575# define GETTEXT_DLL_ALT1 "libintl-8.dll"
576# define GETTEXT_DLL_ALT2 "intl.dll"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000577# endif
Bram Moolenaarf6a2b082012-06-29 13:14:03 +0200578/* Dummy functions */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000579static char *null_libintl_gettext(const char *);
Bram Moolenaaree695f72016-08-03 22:08:45 +0200580static char *null_libintl_ngettext(const char *, const char *, unsigned long n);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000581static char *null_libintl_textdomain(const char *);
582static char *null_libintl_bindtextdomain(const char *, const char *);
583static char *null_libintl_bind_textdomain_codeset(const char *, const char *);
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100584static int null_libintl_putenv(const char *);
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100585static int null_libintl_wputenv(const wchar_t *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000586
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200587static HINSTANCE hLibintlDLL = NULL;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000588char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
Bram Moolenaaree695f72016-08-03 22:08:45 +0200589char *(*dyn_libintl_ngettext)(const char *, const char *, unsigned long n)
590 = null_libintl_ngettext;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000591char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
592char *(*dyn_libintl_bindtextdomain)(const char *, const char *)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000593 = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000594char *(*dyn_libintl_bind_textdomain_codeset)(const char *, const char *)
595 = null_libintl_bind_textdomain_codeset;
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100596int (*dyn_libintl_putenv)(const char *) = null_libintl_putenv;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100597int (*dyn_libintl_wputenv)(const wchar_t *) = null_libintl_wputenv;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000598
599 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100600dyn_libintl_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000601{
602 int i;
603 static struct
604 {
605 char *name;
606 FARPROC *ptr;
607 } libintl_entry[] =
608 {
609 {"gettext", (FARPROC*)&dyn_libintl_gettext},
Bram Moolenaaree695f72016-08-03 22:08:45 +0200610 {"ngettext", (FARPROC*)&dyn_libintl_ngettext},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000611 {"textdomain", (FARPROC*)&dyn_libintl_textdomain},
612 {"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
613 {NULL, NULL}
614 };
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100615 HINSTANCE hmsvcrt;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000616
Bram Moolenaar7554c542018-10-06 15:03:15 +0200617 // No need to initialize twice.
618 if (hLibintlDLL != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000619 return 1;
Bram Moolenaar7554c542018-10-06 15:03:15 +0200620 // Load gettext library (libintl.dll and other names).
Bram Moolenaar923e43b2016-01-28 15:07:38 +0100621 hLibintlDLL = vimLoadLib(GETTEXT_DLL);
Bram Moolenaar7554c542018-10-06 15:03:15 +0200622#ifdef GETTEXT_DLL_ALT1
Bram Moolenaar286eacd2016-01-16 18:05:50 +0100623 if (!hLibintlDLL)
Bram Moolenaar7554c542018-10-06 15:03:15 +0200624 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT1);
625#endif
626#ifdef GETTEXT_DLL_ALT2
627 if (!hLibintlDLL)
628 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT2);
Bram Moolenaar938ee832016-01-24 15:36:03 +0100629#endif
630 if (!hLibintlDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000631 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200632 if (p_verbose > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000633 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200634 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100635 semsg(_(e_loadlib), GETTEXT_DLL);
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200636 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000637 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200638 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000639 }
640 for (i = 0; libintl_entry[i].name != NULL
641 && libintl_entry[i].ptr != NULL; ++i)
642 {
643 if ((*libintl_entry[i].ptr = (FARPROC)GetProcAddress(hLibintlDLL,
644 libintl_entry[i].name)) == NULL)
645 {
646 dyn_libintl_end();
647 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000648 {
649 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100650 semsg(_(e_loadfunc), libintl_entry[i].name);
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000651 verbose_leave();
652 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000653 return 0;
654 }
655 }
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000656
657 /* The bind_textdomain_codeset() function is optional. */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000658 dyn_libintl_bind_textdomain_codeset = (void *)GetProcAddress(hLibintlDLL,
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000659 "bind_textdomain_codeset");
660 if (dyn_libintl_bind_textdomain_codeset == NULL)
661 dyn_libintl_bind_textdomain_codeset =
662 null_libintl_bind_textdomain_codeset;
663
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100664 /* _putenv() function for the libintl.dll is optional. */
665 hmsvcrt = find_imported_module_by_funcname(hLibintlDLL, "getenv");
666 if (hmsvcrt != NULL)
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100667 {
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100668 dyn_libintl_putenv = (void *)GetProcAddress(hmsvcrt, "_putenv");
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100669 dyn_libintl_wputenv = (void *)GetProcAddress(hmsvcrt, "_wputenv");
670 }
671 if (dyn_libintl_putenv == NULL || dyn_libintl_putenv == _putenv)
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100672 dyn_libintl_putenv = null_libintl_putenv;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100673 if (dyn_libintl_wputenv == NULL || dyn_libintl_wputenv == _wputenv)
674 dyn_libintl_wputenv = null_libintl_wputenv;
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100675
Bram Moolenaar071d4272004-06-13 20:20:40 +0000676 return 1;
677}
678
679 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100680dyn_libintl_end(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000681{
682 if (hLibintlDLL)
683 FreeLibrary(hLibintlDLL);
684 hLibintlDLL = NULL;
685 dyn_libintl_gettext = null_libintl_gettext;
Bram Moolenaaree695f72016-08-03 22:08:45 +0200686 dyn_libintl_ngettext = null_libintl_ngettext;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000687 dyn_libintl_textdomain = null_libintl_textdomain;
688 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000689 dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset;
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100690 dyn_libintl_putenv = null_libintl_putenv;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100691 dyn_libintl_wputenv = null_libintl_wputenv;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000692}
693
694 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000695null_libintl_gettext(const char *msgid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000696{
697 return (char*)msgid;
698}
699
700 static char *
Bram Moolenaaree695f72016-08-03 22:08:45 +0200701null_libintl_ngettext(
702 const char *msgid,
703 const char *msgid_plural,
704 unsigned long n)
705{
Bram Moolenaarc90f2ae2016-08-04 22:00:15 +0200706 return (char *)(n == 1 ? msgid : msgid_plural);
Bram Moolenaaree695f72016-08-03 22:08:45 +0200707}
708
Bram Moolenaaree695f72016-08-03 22:08:45 +0200709 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100710null_libintl_bindtextdomain(
711 const char *domainname UNUSED,
712 const char *dirname UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000713{
714 return NULL;
715}
716
717 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100718null_libintl_bind_textdomain_codeset(
719 const char *domainname UNUSED,
720 const char *codeset UNUSED)
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000721{
722 return NULL;
723}
724
725 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100726null_libintl_textdomain(const char *domainname UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000727{
728 return NULL;
729}
730
Bram Moolenaare0ab9792017-08-02 23:18:25 +0200731 static int
Bram Moolenaar1266d672017-02-01 13:43:36 +0100732null_libintl_putenv(const char *envstring UNUSED)
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100733{
734 return 0;
735}
736
Bram Moolenaare0ab9792017-08-02 23:18:25 +0200737 static int
Bram Moolenaar1266d672017-02-01 13:43:36 +0100738null_libintl_wputenv(const wchar_t *envstring UNUSED)
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100739{
740 return 0;
741}
742
Bram Moolenaar071d4272004-06-13 20:20:40 +0000743#endif /* DYNAMIC_GETTEXT */
744
745/* This symbol is not defined in older versions of the SDK or Visual C++ */
746
747#ifndef VER_PLATFORM_WIN32_WINDOWS
748# define VER_PLATFORM_WIN32_WINDOWS 1
749#endif
750
751DWORD g_PlatformId;
752
753#ifdef HAVE_ACL
Bram Moolenaar82881492012-11-20 16:53:39 +0100754# ifndef PROTO
755# include <aclapi.h>
756# endif
Bram Moolenaar27515922013-06-29 15:36:26 +0200757# ifndef PROTECTED_DACL_SECURITY_INFORMATION
758# define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000L
759# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000760#endif
761
Bram Moolenaar27515922013-06-29 15:36:26 +0200762#ifdef HAVE_ACL
763/*
764 * Enables or disables the specified privilege.
765 */
766 static BOOL
767win32_enable_privilege(LPTSTR lpszPrivilege, BOOL bEnable)
768{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100769 BOOL bResult;
770 LUID luid;
771 HANDLE hToken;
772 TOKEN_PRIVILEGES tokenPrivileges;
Bram Moolenaar27515922013-06-29 15:36:26 +0200773
774 if (!OpenProcessToken(GetCurrentProcess(),
775 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
776 return FALSE;
777
778 if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
779 {
780 CloseHandle(hToken);
781 return FALSE;
782 }
783
Bram Moolenaar45500912014-07-09 20:51:07 +0200784 tokenPrivileges.PrivilegeCount = 1;
Bram Moolenaar27515922013-06-29 15:36:26 +0200785 tokenPrivileges.Privileges[0].Luid = luid;
786 tokenPrivileges.Privileges[0].Attributes = bEnable ?
787 SE_PRIVILEGE_ENABLED : 0;
788
789 bResult = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges,
790 sizeof(TOKEN_PRIVILEGES), NULL, NULL);
791
792 CloseHandle(hToken);
793
794 return bResult && GetLastError() == ERROR_SUCCESS;
795}
796#endif
797
Bram Moolenaar071d4272004-06-13 20:20:40 +0000798/*
799 * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
800 * VER_PLATFORM_WIN32_WINDOWS (Win95).
801 */
802 void
803PlatformId(void)
804{
805 static int done = FALSE;
806
807 if (!done)
808 {
809 OSVERSIONINFO ovi;
810
811 ovi.dwOSVersionInfoSize = sizeof(ovi);
812 GetVersionEx(&ovi);
813
814 g_PlatformId = ovi.dwPlatformId;
815
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100816 if ((ovi.dwMajorVersion == 6 && ovi.dwMinorVersion >= 2)
817 || ovi.dwMajorVersion > 6)
818 win8_or_later = TRUE;
819
Bram Moolenaar071d4272004-06-13 20:20:40 +0000820#ifdef HAVE_ACL
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200821 /* Enable privilege for getting or setting SACLs. */
822 win32_enable_privilege(SE_SECURITY_NAME, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000823#endif
824 done = TRUE;
825 }
826}
827
Bram Moolenaar071d4272004-06-13 20:20:40 +0000828#ifndef FEAT_GUI_W32
829
830#define SHIFT (SHIFT_PRESSED)
831#define CTRL (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
832#define ALT (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
833#define ALT_GR (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)
834
835
836/* When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
837 * We map function keys to their ANSI terminal equivalents, as produced
838 * by ANSI.SYS, for compatibility with the MS-DOS version of Vim. Any
839 * ANSI key with a value >= '\300' is nonstandard, but provided anyway
840 * so that the user can have access to all SHIFT-, CTRL-, and ALT-
841 * combinations of function/arrow/etc keys.
842 */
843
Bram Moolenaard6f676d2005-06-01 21:51:55 +0000844static const struct
Bram Moolenaar071d4272004-06-13 20:20:40 +0000845{
846 WORD wVirtKey;
847 BOOL fAnsiKey;
848 int chAlone;
849 int chShift;
850 int chCtrl;
851 int chAlt;
852} VirtKeyMap[] =
853{
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200854// Key ANSI alone shift ctrl alt
Bram Moolenaar071d4272004-06-13 20:20:40 +0000855 { VK_ESCAPE,FALSE, ESC, ESC, ESC, ESC, },
856
857 { VK_F1, TRUE, ';', 'T', '^', 'h', },
858 { VK_F2, TRUE, '<', 'U', '_', 'i', },
859 { VK_F3, TRUE, '=', 'V', '`', 'j', },
860 { VK_F4, TRUE, '>', 'W', 'a', 'k', },
861 { VK_F5, TRUE, '?', 'X', 'b', 'l', },
862 { VK_F6, TRUE, '@', 'Y', 'c', 'm', },
863 { VK_F7, TRUE, 'A', 'Z', 'd', 'n', },
864 { VK_F8, TRUE, 'B', '[', 'e', 'o', },
865 { VK_F9, TRUE, 'C', '\\', 'f', 'p', },
866 { VK_F10, TRUE, 'D', ']', 'g', 'q', },
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200867 { VK_F11, TRUE, '\205', '\207', '\211', '\213', },
868 { VK_F12, TRUE, '\206', '\210', '\212', '\214', },
Bram Moolenaar071d4272004-06-13 20:20:40 +0000869
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200870 { VK_HOME, TRUE, 'G', '\302', 'w', '\303', },
871 { VK_UP, TRUE, 'H', '\304', '\305', '\306', },
872 { VK_PRIOR, TRUE, 'I', '\307', '\204', '\310', }, // PgUp
873 { VK_LEFT, TRUE, 'K', '\311', 's', '\312', },
874 { VK_RIGHT, TRUE, 'M', '\313', 't', '\314', },
875 { VK_END, TRUE, 'O', '\315', 'u', '\316', },
876 { VK_DOWN, TRUE, 'P', '\317', '\320', '\321', },
877 { VK_NEXT, TRUE, 'Q', '\322', 'v', '\323', }, // PgDn
878 { VK_INSERT,TRUE, 'R', '\324', '\325', '\326', },
879 { VK_DELETE,TRUE, 'S', '\327', '\330', '\331', },
Bram Moolenaar071d4272004-06-13 20:20:40 +0000880
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200881 { VK_SNAPSHOT,TRUE, 0, 0, 0, 'r', }, // PrtScrn
Bram Moolenaar071d4272004-06-13 20:20:40 +0000882
883#if 0
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200884 // Most people don't have F13-F20, but what the hell...
885 { VK_F13, TRUE, '\332', '\333', '\334', '\335', },
886 { VK_F14, TRUE, '\336', '\337', '\340', '\341', },
887 { VK_F15, TRUE, '\342', '\343', '\344', '\345', },
888 { VK_F16, TRUE, '\346', '\347', '\350', '\351', },
889 { VK_F17, TRUE, '\352', '\353', '\354', '\355', },
890 { VK_F18, TRUE, '\356', '\357', '\360', '\361', },
891 { VK_F19, TRUE, '\362', '\363', '\364', '\365', },
892 { VK_F20, TRUE, '\366', '\367', '\370', '\371', },
Bram Moolenaar071d4272004-06-13 20:20:40 +0000893#endif
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200894 { VK_ADD, TRUE, 'N', 'N', 'N', 'N', }, // keyp '+'
895 { VK_SUBTRACT, TRUE,'J', 'J', 'J', 'J', }, // keyp '-'
896 // { VK_DIVIDE, TRUE,'N', 'N', 'N', 'N', }, // keyp '/'
897 { VK_MULTIPLY, TRUE,'7', '7', '7', '7', }, // keyp '*'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000898
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200899 { VK_NUMPAD0,TRUE, '\332', '\333', '\334', '\335', },
900 { VK_NUMPAD1,TRUE, '\336', '\337', '\340', '\341', },
901 { VK_NUMPAD2,TRUE, '\342', '\343', '\344', '\345', },
902 { VK_NUMPAD3,TRUE, '\346', '\347', '\350', '\351', },
903 { VK_NUMPAD4,TRUE, '\352', '\353', '\354', '\355', },
904 { VK_NUMPAD5,TRUE, '\356', '\357', '\360', '\361', },
905 { VK_NUMPAD6,TRUE, '\362', '\363', '\364', '\365', },
906 { VK_NUMPAD7,TRUE, '\366', '\367', '\370', '\371', },
907 { VK_NUMPAD8,TRUE, '\372', '\373', '\374', '\375', },
908 // Sorry, out of number space! <negri>
909 { VK_NUMPAD9,TRUE, '\376', '\377', '\377', '\367', },
910
Bram Moolenaar071d4272004-06-13 20:20:40 +0000911};
912
913
914#ifdef _MSC_VER
915// The ToAscii bug destroys several registers. Need to turn off optimization
916// or the GetConsoleKeyboardLayoutName hack will fail in non-debug versions
Bram Moolenaar7b5f8322006-03-23 22:47:08 +0000917# pragma warning(push)
918# pragma warning(disable: 4748)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000919# pragma optimize("", off)
920#endif
921
922#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200923# define UChar UnicodeChar
Bram Moolenaar071d4272004-06-13 20:20:40 +0000924#else
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200925# define UChar uChar.UnicodeChar
Bram Moolenaar071d4272004-06-13 20:20:40 +0000926#endif
927
928/* The return code indicates key code size. */
929 static int
930#ifdef __BORLANDC__
931 __stdcall
932#endif
933win32_kbd_patch_key(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000934 KEY_EVENT_RECORD *pker)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000935{
936 UINT uMods = pker->dwControlKeyState;
937 static int s_iIsDead = 0;
938 static WORD awAnsiCode[2];
939 static BYTE abKeystate[256];
940
941
942 if (s_iIsDead == 2)
943 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200944 pker->UChar = (WCHAR) awAnsiCode[1];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000945 s_iIsDead = 0;
946 return 1;
947 }
948
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200949 if (pker->UChar != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000950 return 1;
951
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200952 vim_memset(abKeystate, 0, sizeof (abKeystate));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000953
Bram Moolenaar071d4272004-06-13 20:20:40 +0000954 /* Clear any pending dead keys */
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200955 ToUnicode(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000956
957 if (uMods & SHIFT_PRESSED)
958 abKeystate[VK_SHIFT] = 0x80;
959 if (uMods & CAPSLOCK_ON)
960 abKeystate[VK_CAPITAL] = 1;
961
962 if ((uMods & ALT_GR) == ALT_GR)
963 {
964 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
965 abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
966 }
967
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200968 s_iIsDead = ToUnicode(pker->wVirtualKeyCode, pker->wVirtualScanCode,
969 abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000970
971 if (s_iIsDead > 0)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200972 pker->UChar = (WCHAR) awAnsiCode[0];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000973
974 return s_iIsDead;
975}
976
977#ifdef _MSC_VER
978/* MUST switch optimization on again here, otherwise a call to
979 * decode_key_event() may crash (e.g. when hitting caps-lock) */
980# pragma optimize("", on)
Bram Moolenaar7b5f8322006-03-23 22:47:08 +0000981# pragma warning(pop)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000982
983# if (_MSC_VER < 1100)
984/* MUST turn off global optimisation for this next function, or
985 * pressing ctrl-minus in insert mode crashes Vim when built with
986 * VC4.1. -- negri. */
987# pragma optimize("g", off)
988# endif
989#endif
990
991static BOOL g_fJustGotFocus = FALSE;
992
993/*
994 * Decode a KEY_EVENT into one or two keystrokes
995 */
996 static BOOL
997decode_key_event(
998 KEY_EVENT_RECORD *pker,
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200999 WCHAR *pch,
1000 WCHAR *pch2,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001001 int *pmodifiers,
1002 BOOL fDoPost)
1003{
1004 int i;
1005 const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
1006
1007 *pch = *pch2 = NUL;
1008 g_fJustGotFocus = FALSE;
1009
1010 /* ignore key up events */
1011 if (!pker->bKeyDown)
1012 return FALSE;
1013
1014 /* ignore some keystrokes */
1015 switch (pker->wVirtualKeyCode)
1016 {
1017 /* modifiers */
1018 case VK_SHIFT:
1019 case VK_CONTROL:
1020 case VK_MENU: /* Alt key */
1021 return FALSE;
1022
1023 default:
1024 break;
1025 }
1026
1027 /* special cases */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001028 if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->UChar == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029 {
1030 /* Ctrl-6 is Ctrl-^ */
1031 if (pker->wVirtualKeyCode == '6')
1032 {
1033 *pch = Ctrl_HAT;
1034 return TRUE;
1035 }
1036 /* Ctrl-2 is Ctrl-@ */
1037 else if (pker->wVirtualKeyCode == '2')
1038 {
1039 *pch = NUL;
1040 return TRUE;
1041 }
1042 /* Ctrl-- is Ctrl-_ */
1043 else if (pker->wVirtualKeyCode == 0xBD)
1044 {
1045 *pch = Ctrl__;
1046 return TRUE;
1047 }
1048 }
1049
1050 /* Shift-TAB */
1051 if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED))
1052 {
1053 *pch = K_NUL;
1054 *pch2 = '\017';
1055 return TRUE;
1056 }
1057
1058 for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]); --i >= 0; )
1059 {
1060 if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
1061 {
1062 if (nModifs == 0)
1063 *pch = VirtKeyMap[i].chAlone;
1064 else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
1065 *pch = VirtKeyMap[i].chShift;
1066 else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
1067 *pch = VirtKeyMap[i].chCtrl;
1068 else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
1069 *pch = VirtKeyMap[i].chAlt;
1070
1071 if (*pch != 0)
1072 {
1073 if (VirtKeyMap[i].fAnsiKey)
1074 {
1075 *pch2 = *pch;
1076 *pch = K_NUL;
1077 }
1078
1079 return TRUE;
1080 }
1081 }
1082 }
1083
1084 i = win32_kbd_patch_key(pker);
1085
1086 if (i < 0)
1087 *pch = NUL;
1088 else
1089 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001090 *pch = (i > 0) ? pker->UChar : NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001091
1092 if (pmodifiers != NULL)
1093 {
1094 /* Pass on the ALT key as a modifier, but only when not combined
1095 * with CTRL (which is ALTGR). */
1096 if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0)
1097 *pmodifiers |= MOD_MASK_ALT;
1098
1099 /* Pass on SHIFT only for special keys, because we don't know when
1100 * it's already included with the character. */
1101 if ((nModifs & SHIFT) != 0 && *pch <= 0x20)
1102 *pmodifiers |= MOD_MASK_SHIFT;
1103
1104 /* Pass on CTRL only for non-special keys, because we don't know
1105 * when it's already included with the character. And not when
1106 * combined with ALT (which is ALTGR). */
1107 if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0
1108 && *pch >= 0x20 && *pch < 0x80)
1109 *pmodifiers |= MOD_MASK_CTRL;
1110 }
1111 }
1112
1113 return (*pch != NUL);
1114}
1115
1116#ifdef _MSC_VER
1117# pragma optimize("", on)
1118#endif
1119
1120#endif /* FEAT_GUI_W32 */
1121
1122
1123#ifdef FEAT_MOUSE
1124
1125/*
1126 * For the GUI the mouse handling is in gui_w32.c.
1127 */
1128# ifdef FEAT_GUI_W32
1129 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01001130mch_setmouse(int on UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001131{
1132}
1133# else
1134static int g_fMouseAvail = FALSE; /* mouse present */
1135static int g_fMouseActive = FALSE; /* mouse enabled */
1136static int g_nMouseClick = -1; /* mouse status */
1137static int g_xMouse; /* mouse x coordinate */
1138static int g_yMouse; /* mouse y coordinate */
1139
1140/*
1141 * Enable or disable mouse input
1142 */
1143 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001144mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001145{
1146 DWORD cmodein;
1147
1148 if (!g_fMouseAvail)
1149 return;
1150
1151 g_fMouseActive = on;
1152 GetConsoleMode(g_hConIn, &cmodein);
1153
1154 if (g_fMouseActive)
1155 cmodein |= ENABLE_MOUSE_INPUT;
1156 else
1157 cmodein &= ~ENABLE_MOUSE_INPUT;
1158
1159 SetConsoleMode(g_hConIn, cmodein);
1160}
1161
Bram Moolenaar157d8132018-03-06 17:09:20 +01001162
1163#if defined(FEAT_BEVAL_TERM) || defined(PROTO)
1164/*
1165 * Called when 'balloonevalterm' changed.
1166 */
1167 void
1168mch_bevalterm_changed(void)
1169{
1170 mch_setmouse(g_fMouseActive);
1171}
1172#endif
1173
Bram Moolenaar071d4272004-06-13 20:20:40 +00001174/*
1175 * Decode a MOUSE_EVENT. If it's a valid event, return MOUSE_LEFT,
1176 * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
1177 * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
1178 * or a MOUSE_LEFT, _MIDDLE, or _RIGHT. We encode the button type,
1179 * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
1180 * and we return the mouse position in g_xMouse and g_yMouse.
1181 *
1182 * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
1183 * MOUSE_DRAGs and one MOUSE_RELEASE. MOUSE_RELEASE will be followed only
1184 * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
1185 *
1186 * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
1187 * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
1188 *
1189 * Windows will send us MOUSE_MOVED notifications whenever the mouse
1190 * moves, even if it stays within the same character cell. We ignore
1191 * all MOUSE_MOVED messages if the position hasn't really changed, and
1192 * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
1193 * we're only interested in MOUSE_DRAG).
1194 *
1195 * All of this is complicated by the code that fakes MOUSE_MIDDLE on
1196 * 2-button mouses by pressing the left & right buttons simultaneously.
1197 * In practice, it's almost impossible to click both at the same time,
1198 * so we need to delay a little. Also, we tend not to get MOUSE_RELEASE
1199 * in such cases, if the user is clicking quickly.
1200 */
1201 static BOOL
1202decode_mouse_event(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001203 MOUSE_EVENT_RECORD *pmer)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204{
1205 static int s_nOldButton = -1;
1206 static int s_nOldMouseClick = -1;
1207 static int s_xOldMouse = -1;
1208 static int s_yOldMouse = -1;
1209 static linenr_T s_old_topline = 0;
1210#ifdef FEAT_DIFF
1211 static int s_old_topfill = 0;
1212#endif
1213 static int s_cClicks = 1;
1214 static BOOL s_fReleased = TRUE;
1215 static DWORD s_dwLastClickTime = 0;
1216 static BOOL s_fNextIsMiddle = FALSE;
1217
1218 static DWORD cButtons = 0; /* number of buttons supported */
1219
1220 const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
1221 const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
1222 const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
1223 const DWORD LEFT_RIGHT = LEFT | RIGHT;
1224
1225 int nButton;
1226
1227 if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons))
1228 cButtons = 2;
1229
1230 if (!g_fMouseAvail || !g_fMouseActive)
1231 {
1232 g_nMouseClick = -1;
1233 return FALSE;
1234 }
1235
1236 /* get a spurious MOUSE_EVENT immediately after receiving focus; ignore */
1237 if (g_fJustGotFocus)
1238 {
1239 g_fJustGotFocus = FALSE;
1240 return FALSE;
1241 }
1242
1243 /* unprocessed mouse click? */
1244 if (g_nMouseClick != -1)
1245 return TRUE;
1246
1247 nButton = -1;
1248 g_xMouse = pmer->dwMousePosition.X;
1249 g_yMouse = pmer->dwMousePosition.Y;
1250
1251 if (pmer->dwEventFlags == MOUSE_MOVED)
1252 {
Bram Moolenaar157d8132018-03-06 17:09:20 +01001253 /* Ignore MOUSE_MOVED events if (x, y) hasn't changed. (We get these
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 * events even when the mouse moves only within a char cell.) */
1255 if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse)
1256 return FALSE;
1257 }
1258
1259 /* If no buttons are pressed... */
1260 if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0)
1261 {
Bram Moolenaar157d8132018-03-06 17:09:20 +01001262 nButton = MOUSE_RELEASE;
1263
Bram Moolenaar071d4272004-06-13 20:20:40 +00001264 /* If the last thing returned was MOUSE_RELEASE, ignore this */
1265 if (s_fReleased)
Bram Moolenaar157d8132018-03-06 17:09:20 +01001266 {
1267#ifdef FEAT_BEVAL_TERM
1268 /* do return mouse move events when we want them */
1269 if (p_bevalterm)
1270 nButton = MOUSE_DRAG;
1271 else
1272#endif
1273 return FALSE;
1274 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001275
Bram Moolenaar071d4272004-06-13 20:20:40 +00001276 s_fReleased = TRUE;
1277 }
1278 else /* one or more buttons pressed */
1279 {
1280 /* on a 2-button mouse, hold down left and right buttons
1281 * simultaneously to get MIDDLE. */
1282
1283 if (cButtons == 2 && s_nOldButton != MOUSE_DRAG)
1284 {
1285 DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
1286
1287 /* if either left or right button only is pressed, see if the
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02001288 * next mouse event has both of them pressed */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001289 if (dwLR == LEFT || dwLR == RIGHT)
1290 {
1291 for (;;)
1292 {
1293 /* wait a short time for next input event */
1294 if (WaitForSingleObject(g_hConIn, p_mouset / 3)
1295 != WAIT_OBJECT_0)
1296 break;
1297 else
1298 {
1299 DWORD cRecords = 0;
1300 INPUT_RECORD ir;
1301 MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
1302
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001303 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001304
1305 if (cRecords == 0 || ir.EventType != MOUSE_EVENT
1306 || !(pmer2->dwButtonState & LEFT_RIGHT))
1307 break;
1308 else
1309 {
1310 if (pmer2->dwEventFlags != MOUSE_MOVED)
1311 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001312 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001313
1314 return decode_mouse_event(pmer2);
1315 }
1316 else if (s_xOldMouse == pmer2->dwMousePosition.X &&
1317 s_yOldMouse == pmer2->dwMousePosition.Y)
1318 {
1319 /* throw away spurious mouse move */
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001320 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001321
1322 /* are there any more mouse events in queue? */
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001323 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324
1325 if (cRecords==0 || ir.EventType != MOUSE_EVENT)
1326 break;
1327 }
1328 else
1329 break;
1330 }
1331 }
1332 }
1333 }
1334 }
1335
1336 if (s_fNextIsMiddle)
1337 {
1338 nButton = (pmer->dwEventFlags == MOUSE_MOVED)
1339 ? MOUSE_DRAG : MOUSE_MIDDLE;
1340 s_fNextIsMiddle = FALSE;
1341 }
1342 else if (cButtons == 2 &&
1343 ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
1344 {
1345 nButton = MOUSE_MIDDLE;
1346
1347 if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED)
1348 {
1349 s_fNextIsMiddle = TRUE;
1350 nButton = MOUSE_RELEASE;
1351 }
1352 }
1353 else if ((pmer->dwButtonState & LEFT) == LEFT)
1354 nButton = MOUSE_LEFT;
1355 else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
1356 nButton = MOUSE_MIDDLE;
1357 else if ((pmer->dwButtonState & RIGHT) == RIGHT)
1358 nButton = MOUSE_RIGHT;
1359
1360 if (! s_fReleased && ! s_fNextIsMiddle
1361 && nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG)
1362 return FALSE;
1363
1364 s_fReleased = s_fNextIsMiddle;
1365 }
1366
1367 if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK)
1368 {
1369 /* button pressed or released, without mouse moving */
1370 if (nButton != -1 && nButton != MOUSE_RELEASE)
1371 {
1372 DWORD dwCurrentTime = GetTickCount();
1373
1374 if (s_xOldMouse != g_xMouse
1375 || s_yOldMouse != g_yMouse
1376 || s_nOldButton != nButton
1377 || s_old_topline != curwin->w_topline
1378#ifdef FEAT_DIFF
1379 || s_old_topfill != curwin->w_topfill
1380#endif
1381 || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset)
1382 {
1383 s_cClicks = 1;
1384 }
1385 else if (++s_cClicks > 4)
1386 {
1387 s_cClicks = 1;
1388 }
1389
1390 s_dwLastClickTime = dwCurrentTime;
1391 }
1392 }
1393 else if (pmer->dwEventFlags == MOUSE_MOVED)
1394 {
1395 if (nButton != -1 && nButton != MOUSE_RELEASE)
1396 nButton = MOUSE_DRAG;
1397
1398 s_cClicks = 1;
1399 }
1400
1401 if (nButton == -1)
1402 return FALSE;
1403
1404 if (nButton != MOUSE_RELEASE)
1405 s_nOldButton = nButton;
1406
1407 g_nMouseClick = nButton;
1408
1409 if (pmer->dwControlKeyState & SHIFT_PRESSED)
1410 g_nMouseClick |= MOUSE_SHIFT;
1411 if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
1412 g_nMouseClick |= MOUSE_CTRL;
1413 if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
1414 g_nMouseClick |= MOUSE_ALT;
1415
1416 if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE)
1417 SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
1418
1419 /* only pass on interesting (i.e., different) mouse events */
1420 if (s_xOldMouse == g_xMouse
1421 && s_yOldMouse == g_yMouse
1422 && s_nOldMouseClick == g_nMouseClick)
1423 {
1424 g_nMouseClick = -1;
1425 return FALSE;
1426 }
1427
1428 s_xOldMouse = g_xMouse;
1429 s_yOldMouse = g_yMouse;
1430 s_old_topline = curwin->w_topline;
1431#ifdef FEAT_DIFF
1432 s_old_topfill = curwin->w_topfill;
1433#endif
1434 s_nOldMouseClick = g_nMouseClick;
1435
1436 return TRUE;
1437}
1438
1439# endif /* FEAT_GUI_W32 */
1440#endif /* FEAT_MOUSE */
1441
1442
1443#ifdef MCH_CURSOR_SHAPE
1444/*
1445 * Set the shape of the cursor.
1446 * 'thickness' can be from 1 (thin) to 99 (block)
1447 */
1448 static void
1449mch_set_cursor_shape(int thickness)
1450{
1451 CONSOLE_CURSOR_INFO ConsoleCursorInfo;
1452 ConsoleCursorInfo.dwSize = thickness;
1453 ConsoleCursorInfo.bVisible = s_cursor_visible;
1454
1455 SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo);
1456 if (s_cursor_visible)
1457 SetConsoleCursorPosition(g_hConOut, g_coord);
1458}
1459
1460 void
1461mch_update_cursor(void)
1462{
1463 int idx;
1464 int thickness;
1465
1466 /*
1467 * How the cursor is drawn depends on the current mode.
1468 */
1469 idx = get_shape_idx(FALSE);
1470
1471 if (shape_table[idx].shape == SHAPE_BLOCK)
1472 thickness = 99; /* 100 doesn't work on W95 */
1473 else
1474 thickness = shape_table[idx].percentage;
1475 mch_set_cursor_shape(thickness);
1476}
1477#endif
1478
1479#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1480/*
1481 * Handle FOCUS_EVENT.
1482 */
1483 static void
1484handle_focus_event(INPUT_RECORD ir)
1485{
1486 g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
1487 ui_focus_change((int)g_fJustGotFocus);
1488}
1489
1490/*
1491 * Wait until console input from keyboard or mouse is available,
1492 * or the time is up.
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001493 * When "ignore_input" is TRUE even wait when input is available.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001494 * Return TRUE if something is available FALSE if not.
1495 */
1496 static int
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001497WaitForChar(long msec, int ignore_input)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498{
1499 DWORD dwNow = 0, dwEndTime = 0;
1500 INPUT_RECORD ir;
1501 DWORD cRecords;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001502 WCHAR ch, ch2;
Bram Moolenaar4445f7e2016-04-20 20:55:56 +02001503#ifdef FEAT_TIMERS
Bram Moolenaar40b1b542016-04-20 20:18:23 +02001504 int tb_change_cnt = typebuf.tb_change_cnt;
Bram Moolenaar4445f7e2016-04-20 20:55:56 +02001505#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506
1507 if (msec > 0)
1508 /* Wait until the specified time has elapsed. */
1509 dwEndTime = GetTickCount() + msec;
1510 else if (msec < 0)
1511 /* Wait forever. */
1512 dwEndTime = INFINITE;
1513
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01001514 // We need to loop until the end of the time period, because
1515 // we might get multiple unusable mouse events in that time.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001516 for (;;)
1517 {
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001518 // Only process messages when waiting.
1519 if (msec != 0)
1520 {
Bram Moolenaarca568ae2016-02-01 21:32:58 +01001521#ifdef MESSAGE_QUEUE
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001522 parse_queued_messages();
Bram Moolenaarca568ae2016-02-01 21:32:58 +01001523#endif
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001524#ifdef FEAT_MZSCHEME
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001525 mzvim_check_threads();
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001526#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001527#ifdef FEAT_CLIENTSERVER
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001528 serverProcessPendingMessages();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001529#endif
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001530 }
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001531
Bram Moolenaar071d4272004-06-13 20:20:40 +00001532 if (0
1533#ifdef FEAT_MOUSE
1534 || g_nMouseClick != -1
1535#endif
1536#ifdef FEAT_CLIENTSERVER
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001537 || (!ignore_input && input_available())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001538#endif
1539 )
1540 return TRUE;
1541
1542 if (msec > 0)
1543 {
Bram Moolenaarb7512b72013-08-10 12:45:09 +02001544 /* If the specified wait time has passed, return. Beware that
1545 * GetTickCount() may wrap around (overflow). */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546 dwNow = GetTickCount();
Bram Moolenaarb7512b72013-08-10 12:45:09 +02001547 if ((int)(dwNow - dwEndTime) >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001548 break;
1549 }
1550 if (msec != 0)
1551 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001552 DWORD dwWaitTime = dwEndTime - dwNow;
1553
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01001554#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar8a8199e2016-11-26 15:13:33 +01001555 /* Check channel while waiting for input. */
Bram Moolenaar9186a272016-02-23 19:34:01 +01001556 if (dwWaitTime > 100)
Bram Moolenaar8a8199e2016-11-26 15:13:33 +01001557 {
Bram Moolenaar9186a272016-02-23 19:34:01 +01001558 dwWaitTime = 100;
Bram Moolenaar8a8199e2016-11-26 15:13:33 +01001559 /* If there is readahead then parse_queued_messages() timed out
1560 * and we should call it again soon. */
1561 if (channel_any_readahead())
1562 dwWaitTime = 10;
1563 }
Bram Moolenaar9186a272016-02-23 19:34:01 +01001564#endif
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01001565#ifdef FEAT_BEVAL_GUI
Bram Moolenaar59716a22017-03-01 20:32:44 +01001566 if (p_beval && dwWaitTime > 100)
1567 /* The 'balloonexpr' may indirectly invoke a callback while
1568 * waiting for a character, need to check often. */
1569 dwWaitTime = 100;
1570#endif
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001571#ifdef FEAT_MZSCHEME
1572 if (mzthreads_allowed() && p_mzq > 0
1573 && (msec < 0 || (long)dwWaitTime > p_mzq))
1574 dwWaitTime = p_mzq; /* don't wait longer than 'mzquantum' */
1575#endif
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001576#ifdef FEAT_TIMERS
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001577 // When waiting very briefly don't trigger timers.
1578 if (dwWaitTime > 10)
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001579 {
1580 long due_time;
1581
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001582 // Trigger timers and then get the time in msec until the next
1583 // one is due. Wait up to that time.
1584 due_time = check_due_timer();
1585 if (typebuf.tb_change_cnt != tb_change_cnt)
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001586 {
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001587 // timer may have used feedkeys().
1588 return FALSE;
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001589 }
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001590 if (due_time > 0 && dwWaitTime > (DWORD)due_time)
1591 dwWaitTime = due_time;
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001592 }
1593#endif
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001594 if (
Bram Moolenaar071d4272004-06-13 20:20:40 +00001595#ifdef FEAT_CLIENTSERVER
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001596 // Wait for either an event on the console input or a
1597 // message in the client-server window.
1598 msg_wait_for_multiple_objects(1, &g_hConIn, FALSE,
1599 dwWaitTime, QS_SENDMESSAGE) != WAIT_OBJECT_0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001600#else
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001601 wait_for_single_object(g_hConIn, dwWaitTime)
1602 != WAIT_OBJECT_0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603#endif
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001604 )
1605 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001606 }
1607
1608 cRecords = 0;
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001609 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001610
1611#ifdef FEAT_MBYTE_IME
1612 if (State & CMDLINE && msg_row == Rows - 1)
1613 {
1614 CONSOLE_SCREEN_BUFFER_INFO csbi;
1615
1616 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1617 {
1618 if (csbi.dwCursorPosition.Y != msg_row)
1619 {
1620 /* The screen is now messed up, must redraw the
1621 * command line and later all the windows. */
1622 redraw_all_later(CLEAR);
1623 cmdline_row -= (msg_row - csbi.dwCursorPosition.Y);
1624 redrawcmd();
1625 }
1626 }
1627 }
1628#endif
1629
1630 if (cRecords > 0)
1631 {
1632 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
1633 {
1634#ifdef FEAT_MBYTE_IME
1635 /* Windows IME sends two '\n's with only one 'ENTER'. First:
1636 * wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001637 if (ir.Event.KeyEvent.UChar == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001638 && ir.Event.KeyEvent.wVirtualKeyCode == 13)
1639 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001640 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001641 continue;
1642 }
1643#endif
1644 if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
1645 NULL, FALSE))
1646 return TRUE;
1647 }
1648
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001649 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001650
1651 if (ir.EventType == FOCUS_EVENT)
1652 handle_focus_event(ir);
1653 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
Bram Moolenaarc33ecb22018-02-11 16:40:45 +01001654 {
1655 /* Only call shell_resized() when the size actually change to
1656 * avoid the screen is cleard. */
1657 if (ir.Event.WindowBufferSizeEvent.dwSize.X != Columns
1658 || ir.Event.WindowBufferSizeEvent.dwSize.Y != Rows)
1659 shell_resized();
1660 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001661#ifdef FEAT_MOUSE
1662 else if (ir.EventType == MOUSE_EVENT
1663 && decode_mouse_event(&ir.Event.MouseEvent))
1664 return TRUE;
1665#endif
1666 }
1667 else if (msec == 0)
1668 break;
1669 }
1670
1671#ifdef FEAT_CLIENTSERVER
1672 /* Something might have been received while we were waiting. */
1673 if (input_available())
1674 return TRUE;
1675#endif
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001676
Bram Moolenaar071d4272004-06-13 20:20:40 +00001677 return FALSE;
1678}
1679
1680#ifndef FEAT_GUI_MSWIN
1681/*
1682 * return non-zero if a character is available
1683 */
1684 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001685mch_char_avail(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001686{
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001687 return WaitForChar(0L, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001688}
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001689
1690# if defined(FEAT_TERMINAL) || defined(PROTO)
1691/*
1692 * Check for any pending input or messages.
1693 */
1694 int
1695mch_check_messages(void)
1696{
1697 return WaitForChar(0L, TRUE);
1698}
1699# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001700#endif
1701
1702/*
1703 * Create the console input. Used when reading stdin doesn't work.
1704 */
1705 static void
1706create_conin(void)
1707{
1708 g_hConIn = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
1709 FILE_SHARE_READ|FILE_SHARE_WRITE,
1710 (LPSECURITY_ATTRIBUTES) NULL,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001711 OPEN_EXISTING, 0, (HANDLE)NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001712 did_create_conin = TRUE;
1713}
1714
1715/*
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001716 * Get a keystroke or a mouse event, use a blocking wait.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001717 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001718 static WCHAR
1719tgetch(int *pmodifiers, WCHAR *pch2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720{
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001721 WCHAR ch;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001722
1723 for (;;)
1724 {
1725 INPUT_RECORD ir;
1726 DWORD cRecords = 0;
1727
1728#ifdef FEAT_CLIENTSERVER
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001729 (void)WaitForChar(-1L, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730 if (input_available())
1731 return 0;
1732# ifdef FEAT_MOUSE
1733 if (g_nMouseClick != -1)
1734 return 0;
1735# endif
1736#endif
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001737 if (read_console_input(g_hConIn, &ir, 1, &cRecords) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001738 {
1739 if (did_create_conin)
1740 read_error_exit();
1741 create_conin();
1742 continue;
1743 }
1744
1745 if (ir.EventType == KEY_EVENT)
1746 {
1747 if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2,
1748 pmodifiers, TRUE))
1749 return ch;
1750 }
1751 else if (ir.EventType == FOCUS_EVENT)
1752 handle_focus_event(ir);
1753 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1754 shell_resized();
1755#ifdef FEAT_MOUSE
1756 else if (ir.EventType == MOUSE_EVENT)
1757 {
1758 if (decode_mouse_event(&ir.Event.MouseEvent))
1759 return 0;
1760 }
1761#endif
1762 }
1763}
1764#endif /* !FEAT_GUI_W32 */
1765
1766
1767/*
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02001768 * mch_inchar(): low-level input function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001769 * Get one or more characters from the keyboard or the mouse.
1770 * If time == 0, do not wait for characters.
1771 * If time == n, wait a short time for characters.
1772 * If time == -1, wait forever for characters.
1773 * Returns the number of characters read into buf.
1774 */
1775 int
1776mch_inchar(
Bram Moolenaar1266d672017-02-01 13:43:36 +01001777 char_u *buf UNUSED,
1778 int maxlen UNUSED,
1779 long time UNUSED,
1780 int tb_change_cnt UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001781{
1782#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1783
1784 int len;
1785 int c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786#define TYPEAHEADLEN 20
1787 static char_u typeahead[TYPEAHEADLEN]; /* previously typed bytes. */
1788 static int typeaheadlen = 0;
1789
1790 /* First use any typeahead that was kept because "buf" was too small. */
1791 if (typeaheadlen > 0)
1792 goto theend;
1793
Bram Moolenaar071d4272004-06-13 20:20:40 +00001794 if (time >= 0)
1795 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001796 if (!WaitForChar(time, FALSE)) /* no character available */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001797 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001798 }
1799 else /* time == -1, wait forever */
1800 {
1801 mch_set_winsize_now(); /* Allow winsize changes from now on */
1802
Bram Moolenaar3918c952005-03-15 22:34:55 +00001803 /*
1804 * If there is no character available within 2 seconds (default)
1805 * write the autoscript file to disk. Or cause the CursorHold event
1806 * to be triggered.
1807 */
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001808 if (!WaitForChar(p_ut, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001809 {
Bram Moolenaard35f9712005-12-18 22:02:33 +00001810 if (trigger_cursorhold() && maxlen >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 {
Bram Moolenaar3918c952005-03-15 22:34:55 +00001812 buf[0] = K_SPECIAL;
1813 buf[1] = KS_EXTRA;
1814 buf[2] = (int)KE_CURSORHOLD;
1815 return 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001816 }
Bram Moolenaar702517d2005-06-27 22:34:07 +00001817 before_blocking();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001818 }
1819 }
1820
1821 /*
1822 * Try to read as many characters as there are, until the buffer is full.
1823 */
1824
1825 /* we will get at least one key. Get more if they are available. */
1826 g_fCBrkPressed = FALSE;
1827
1828#ifdef MCH_WRITE_DUMP
1829 if (fdDump)
1830 fputc('[', fdDump);
1831#endif
1832
1833 /* Keep looping until there is something in the typeahead buffer and more
1834 * to get and still room in the buffer (up to two bytes for a char and
1835 * three bytes for a modifier). */
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001836 while ((typeaheadlen == 0 || WaitForChar(0L, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837 && typeaheadlen + 5 <= TYPEAHEADLEN)
1838 {
1839 if (typebuf_changed(tb_change_cnt))
1840 {
1841 /* "buf" may be invalid now if a client put something in the
1842 * typeahead buffer and "buf" is in the typeahead buffer. */
1843 typeaheadlen = 0;
1844 break;
1845 }
1846#ifdef FEAT_MOUSE
1847 if (g_nMouseClick != -1)
1848 {
1849# ifdef MCH_WRITE_DUMP
1850 if (fdDump)
1851 fprintf(fdDump, "{%02x @ %d, %d}",
1852 g_nMouseClick, g_xMouse, g_yMouse);
1853# endif
1854 typeahead[typeaheadlen++] = ESC + 128;
1855 typeahead[typeaheadlen++] = 'M';
1856 typeahead[typeaheadlen++] = g_nMouseClick;
1857 typeahead[typeaheadlen++] = g_xMouse + '!';
1858 typeahead[typeaheadlen++] = g_yMouse + '!';
1859 g_nMouseClick = -1;
1860 }
1861 else
1862#endif
1863 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001864 WCHAR ch2 = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001865 int modifiers = 0;
1866
1867 c = tgetch(&modifiers, &ch2);
1868
1869 if (typebuf_changed(tb_change_cnt))
1870 {
1871 /* "buf" may be invalid now if a client put something in the
1872 * typeahead buffer and "buf" is in the typeahead buffer. */
1873 typeaheadlen = 0;
1874 break;
1875 }
1876
1877 if (c == Ctrl_C && ctrl_c_interrupts)
1878 {
1879#if defined(FEAT_CLIENTSERVER)
1880 trash_input_buf();
1881#endif
1882 got_int = TRUE;
1883 }
1884
1885#ifdef FEAT_MOUSE
1886 if (g_nMouseClick == -1)
1887#endif
1888 {
1889 int n = 1;
1890
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001891 if (ch2 == NUL)
1892 {
1893 int i;
1894 char_u *p;
1895 WCHAR ch[2];
1896
1897 ch[0] = c;
1898 if (c >= 0xD800 && c <= 0xDBFF) /* High surrogate */
1899 {
1900 ch[1] = tgetch(&modifiers, &ch2);
1901 n++;
1902 }
1903 p = utf16_to_enc(ch, &n);
1904 if (p != NULL)
1905 {
1906 for (i = 0; i < n; i++)
1907 typeahead[typeaheadlen + i] = p[i];
1908 vim_free(p);
1909 }
1910 }
1911 else
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001912 typeahead[typeaheadlen] = c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001913 if (ch2 != NUL)
1914 {
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +02001915 if (c == K_NUL)
Bram Moolenaarfeeb4d02017-12-05 15:14:46 +01001916 {
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +02001917 switch (ch2)
1918 {
1919 case (WCHAR)'\324': // SHIFT+Insert
1920 case (WCHAR)'\325': // CTRL+Insert
1921 case (WCHAR)'\327': // SHIFT+Delete
1922 case (WCHAR)'\330': // CTRL+Delete
1923 typeahead[typeaheadlen + n] = (char_u)ch2;
1924 n++;
1925 break;
1926
1927 default:
1928 typeahead[typeaheadlen + n] = 3;
1929 typeahead[typeaheadlen + n + 1] = (char_u)ch2;
1930 n += 2;
1931 break;
1932 }
Bram Moolenaarfeeb4d02017-12-05 15:14:46 +01001933 }
1934 else
1935 {
1936 typeahead[typeaheadlen + n] = 3;
1937 typeahead[typeaheadlen + n + 1] = (char_u)ch2;
1938 n += 2;
1939 }
Bram Moolenaar45500912014-07-09 20:51:07 +02001940 }
1941
Bram Moolenaar071d4272004-06-13 20:20:40 +00001942 /* Use the ALT key to set the 8th bit of the character
1943 * when it's one byte, the 8th bit isn't set yet and not
1944 * using a double-byte encoding (would become a lead
1945 * byte). */
1946 if ((modifiers & MOD_MASK_ALT)
1947 && n == 1
1948 && (typeahead[typeaheadlen] & 0x80) == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001949 && !enc_dbcs
Bram Moolenaar071d4272004-06-13 20:20:40 +00001950 )
1951 {
Bram Moolenaar85a3e5c2007-11-20 16:22:16 +00001952 n = (*mb_char2bytes)(typeahead[typeaheadlen] | 0x80,
1953 typeahead + typeaheadlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001954 modifiers &= ~MOD_MASK_ALT;
1955 }
1956
1957 if (modifiers != 0)
1958 {
1959 /* Prepend modifiers to the character. */
1960 mch_memmove(typeahead + typeaheadlen + 3,
1961 typeahead + typeaheadlen, n);
1962 typeahead[typeaheadlen++] = K_SPECIAL;
1963 typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
1964 typeahead[typeaheadlen++] = modifiers;
1965 }
1966
1967 typeaheadlen += n;
1968
1969#ifdef MCH_WRITE_DUMP
1970 if (fdDump)
1971 fputc(c, fdDump);
1972#endif
1973 }
1974 }
1975 }
1976
1977#ifdef MCH_WRITE_DUMP
1978 if (fdDump)
1979 {
1980 fputs("]\n", fdDump);
1981 fflush(fdDump);
1982 }
1983#endif
1984
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985theend:
1986 /* Move typeahead to "buf", as much as fits. */
1987 len = 0;
1988 while (len < maxlen && typeaheadlen > 0)
1989 {
1990 buf[len++] = typeahead[0];
1991 mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
1992 }
1993 return len;
1994
1995#else /* FEAT_GUI_W32 */
1996 return 0;
1997#endif /* FEAT_GUI_W32 */
1998}
1999
Bram Moolenaar82881492012-11-20 16:53:39 +01002000#ifndef PROTO
2001# ifndef __MINGW32__
2002# include <shellapi.h> /* required for FindExecutable() */
2003# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002004#endif
2005
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002006/*
Bram Moolenaar43663192017-03-05 14:29:12 +01002007 * If "use_path" is TRUE: Return TRUE if "name" is in $PATH.
2008 * If "use_path" is FALSE: Return TRUE if "name" exists.
2009 * When returning TRUE and "path" is not NULL save the path and set "*path" to
2010 * the allocated memory.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00002011 * TODO: Should somehow check if it's really executable.
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002012 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002013 static int
Bram Moolenaar43663192017-03-05 14:29:12 +01002014executable_exists(char *name, char_u **path, int use_path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002015{
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002016 char *dum;
2017 char fname[_MAX_PATH];
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02002018 char *curpath, *newpath;
2019 long n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002020
Bram Moolenaar43663192017-03-05 14:29:12 +01002021 if (!use_path)
2022 {
Bram Moolenaarf7e894d2017-03-05 19:49:13 +01002023 if (mch_getperm((char_u *)name) != -1 && !mch_isdir((char_u *)name))
Bram Moolenaar43663192017-03-05 14:29:12 +01002024 {
2025 if (path != NULL)
Bram Moolenaar066029e2017-03-05 15:19:32 +01002026 {
Bram Moolenaarf7e894d2017-03-05 19:49:13 +01002027 if (mch_isFullName((char_u *)name))
Bram Moolenaar066029e2017-03-05 15:19:32 +01002028 *path = vim_strsave((char_u *)name);
2029 else
2030 *path = FullName_save((char_u *)name, FALSE);
2031 }
Bram Moolenaar43663192017-03-05 14:29:12 +01002032 return TRUE;
2033 }
2034 return FALSE;
2035 }
2036
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002037 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002038 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002039 WCHAR *p = enc_to_utf16((char_u *)name, NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002040 WCHAR fnamew[_MAX_PATH];
2041 WCHAR *dumw;
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02002042 WCHAR *wcurpath, *wnewpath;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002043
2044 if (p != NULL)
2045 {
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02002046 wcurpath = _wgetenv(L"PATH");
2047 wnewpath = (WCHAR*)alloc((unsigned)(wcslen(wcurpath) + 3)
2048 * sizeof(WCHAR));
2049 if (wnewpath == NULL)
2050 return FALSE;
2051 wcscpy(wnewpath, L".;");
2052 wcscat(wnewpath, wcurpath);
2053 n = (long)SearchPathW(wnewpath, p, NULL, _MAX_PATH, fnamew, &dumw);
2054 vim_free(wnewpath);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002055 vim_free(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002056 if (n == 0)
2057 return FALSE;
2058 if (GetFileAttributesW(fnamew) & FILE_ATTRIBUTE_DIRECTORY)
2059 return FALSE;
2060 if (path != NULL)
2061 *path = utf16_to_enc(fnamew, NULL);
2062 return TRUE;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002063 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002064 }
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02002065
2066 curpath = getenv("PATH");
2067 newpath = (char*)alloc((unsigned)(STRLEN(curpath) + 3));
2068 if (newpath == NULL)
2069 return FALSE;
2070 STRCPY(newpath, ".;");
2071 STRCAT(newpath, curpath);
2072 n = (long)SearchPath(newpath, name, NULL, _MAX_PATH, fname, &dum);
2073 vim_free(newpath);
2074 if (n == 0)
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002075 return FALSE;
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002076 if (mch_isdir((char_u *)fname))
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002077 return FALSE;
Bram Moolenaarc7f02552014-04-01 21:00:59 +02002078 if (path != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002079 *path = vim_strsave((char_u *)fname);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002080 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002081}
2082
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002083#if ((defined(__MINGW32__) || defined (__CYGWIN32__)) && \
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02002084 __MSVCRT_VERSION__ >= 0x800) || (defined(_MSC_VER) && _MSC_VER >= 1400)
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002085/*
2086 * Bad parameter handler.
2087 *
2088 * Certain MS CRT functions will intentionally crash when passed invalid
2089 * parameters to highlight possible security holes. Setting this function as
2090 * the bad parameter handler will prevent the crash.
2091 *
2092 * In debug builds the parameters contain CRT information that might help track
2093 * down the source of a problem, but in non-debug builds the arguments are all
2094 * NULL/0. Debug builds will also produce assert dialogs from the CRT, it is
2095 * worth allowing these to make debugging of issues easier.
2096 */
2097 static void
2098bad_param_handler(const wchar_t *expression,
2099 const wchar_t *function,
2100 const wchar_t *file,
2101 unsigned int line,
2102 uintptr_t pReserved)
2103{
2104}
2105
2106# define SET_INVALID_PARAM_HANDLER \
2107 ((void)_set_invalid_parameter_handler(bad_param_handler))
2108#else
2109# define SET_INVALID_PARAM_HANDLER
2110#endif
2111
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112#ifdef FEAT_GUI_W32
2113
2114/*
2115 * GUI version of mch_init().
2116 */
2117 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002118mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002119{
2120#ifndef __MINGW32__
2121 extern int _fmode;
2122#endif
2123
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002124 /* Silently handle invalid parameters to CRT functions */
2125 SET_INVALID_PARAM_HANDLER;
2126
Bram Moolenaar071d4272004-06-13 20:20:40 +00002127 /* Let critical errors result in a failure, not in a dialog box. Required
2128 * for the timestamp test to work on removed floppies. */
2129 SetErrorMode(SEM_FAILCRITICALERRORS);
2130
2131 _fmode = O_BINARY; /* we do our own CR-LF translation */
2132
2133 /* Specify window size. Is there a place to get the default from? */
2134 Rows = 25;
2135 Columns = 80;
2136
2137 /* Look for 'vimrun' */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002138 {
2139 char_u vimrun_location[_MAX_PATH + 4];
2140
2141 /* First try in same directory as gvim.exe */
2142 STRCPY(vimrun_location, exe_name);
2143 STRCPY(gettail(vimrun_location), "vimrun.exe");
2144 if (mch_getperm(vimrun_location) >= 0)
2145 {
2146 if (*skiptowhite(vimrun_location) != NUL)
2147 {
2148 /* Enclose path with white space in double quotes. */
2149 mch_memmove(vimrun_location + 1, vimrun_location,
2150 STRLEN(vimrun_location) + 1);
2151 *vimrun_location = '"';
2152 STRCPY(gettail(vimrun_location), "vimrun\" ");
2153 }
2154 else
2155 STRCPY(gettail(vimrun_location), "vimrun ");
2156
2157 vimrun_path = (char *)vim_strsave(vimrun_location);
2158 s_dont_use_vimrun = FALSE;
2159 }
Bram Moolenaar43663192017-03-05 14:29:12 +01002160 else if (executable_exists("vimrun.exe", NULL, TRUE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002161 s_dont_use_vimrun = FALSE;
2162
2163 /* Don't give the warning for a missing vimrun.exe right now, but only
2164 * when vimrun was supposed to be used. Don't bother people that do
2165 * not need vimrun.exe. */
2166 if (s_dont_use_vimrun)
2167 need_vimrun_warning = TRUE;
2168 }
2169
2170 /*
2171 * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
2172 * Otherwise the default "findstr /n" is used.
2173 */
Bram Moolenaar43663192017-03-05 14:29:12 +01002174 if (!executable_exists("findstr.exe", NULL, TRUE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002175 set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
2176
2177#ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002178 win_clip_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002179#endif
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01002180
2181 vtp_flag_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002182}
2183
2184
2185#else /* FEAT_GUI_W32 */
2186
2187#define SRWIDTH(sr) ((sr).Right - (sr).Left + 1)
2188#define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1)
2189
2190/*
2191 * ClearConsoleBuffer()
2192 * Description:
2193 * Clears the entire contents of the console screen buffer, using the
2194 * specified attribute.
2195 * Returns:
2196 * TRUE on success
2197 */
2198 static BOOL
2199ClearConsoleBuffer(WORD wAttribute)
2200{
2201 CONSOLE_SCREEN_BUFFER_INFO csbi;
2202 COORD coord;
2203 DWORD NumCells, dummy;
2204
2205 if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2206 return FALSE;
2207
2208 NumCells = csbi.dwSize.X * csbi.dwSize.Y;
2209 coord.X = 0;
2210 coord.Y = 0;
2211 if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells,
2212 coord, &dummy))
2213 {
2214 return FALSE;
2215 }
2216 if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells,
2217 coord, &dummy))
2218 {
2219 return FALSE;
2220 }
2221
2222 return TRUE;
2223}
2224
2225/*
2226 * FitConsoleWindow()
2227 * Description:
2228 * Checks if the console window will fit within given buffer dimensions.
2229 * Also, if requested, will shrink the window to fit.
2230 * Returns:
2231 * TRUE on success
2232 */
2233 static BOOL
2234FitConsoleWindow(
2235 COORD dwBufferSize,
2236 BOOL WantAdjust)
2237{
2238 CONSOLE_SCREEN_BUFFER_INFO csbi;
2239 COORD dwWindowSize;
2240 BOOL NeedAdjust = FALSE;
2241
2242 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2243 {
2244 /*
2245 * A buffer resize will fail if the current console window does
2246 * not lie completely within that buffer. To avoid this, we might
2247 * have to move and possibly shrink the window.
2248 */
2249 if (csbi.srWindow.Right >= dwBufferSize.X)
2250 {
2251 dwWindowSize.X = SRWIDTH(csbi.srWindow);
2252 if (dwWindowSize.X > dwBufferSize.X)
2253 dwWindowSize.X = dwBufferSize.X;
2254 csbi.srWindow.Right = dwBufferSize.X - 1;
2255 csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X;
2256 NeedAdjust = TRUE;
2257 }
2258 if (csbi.srWindow.Bottom >= dwBufferSize.Y)
2259 {
2260 dwWindowSize.Y = SRHEIGHT(csbi.srWindow);
2261 if (dwWindowSize.Y > dwBufferSize.Y)
2262 dwWindowSize.Y = dwBufferSize.Y;
2263 csbi.srWindow.Bottom = dwBufferSize.Y - 1;
2264 csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y;
2265 NeedAdjust = TRUE;
2266 }
2267 if (NeedAdjust && WantAdjust)
2268 {
2269 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow))
2270 return FALSE;
2271 }
2272 return TRUE;
2273 }
2274
2275 return FALSE;
2276}
2277
2278typedef struct ConsoleBufferStruct
2279{
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002280 BOOL IsValid;
2281 CONSOLE_SCREEN_BUFFER_INFO Info;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002282 PCHAR_INFO Buffer;
2283 COORD BufferSize;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002284 PSMALL_RECT Regions;
2285 int NumRegions;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002286} ConsoleBuffer;
2287
2288/*
2289 * SaveConsoleBuffer()
2290 * Description:
2291 * Saves important information about the console buffer, including the
2292 * actual buffer contents. The saved information is suitable for later
2293 * restoration by RestoreConsoleBuffer().
2294 * Returns:
2295 * TRUE if all information was saved; FALSE otherwise
2296 * If FALSE, still sets cb->IsValid if buffer characteristics were saved.
2297 */
2298 static BOOL
2299SaveConsoleBuffer(
2300 ConsoleBuffer *cb)
2301{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002302 DWORD NumCells;
2303 COORD BufferCoord;
2304 SMALL_RECT ReadRegion;
2305 WORD Y, Y_incr;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002306 int i, numregions;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002307
Bram Moolenaar071d4272004-06-13 20:20:40 +00002308 if (cb == NULL)
2309 return FALSE;
2310
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002311 if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002312 {
2313 cb->IsValid = FALSE;
2314 return FALSE;
2315 }
2316 cb->IsValid = TRUE;
2317
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002318 /*
2319 * Allocate a buffer large enough to hold the entire console screen
2320 * buffer. If this ConsoleBuffer structure has already been initialized
2321 * with a buffer of the correct size, then just use that one.
2322 */
2323 if (!cb->IsValid || cb->Buffer == NULL ||
2324 cb->BufferSize.X != cb->Info.dwSize.X ||
2325 cb->BufferSize.Y != cb->Info.dwSize.Y)
2326 {
2327 cb->BufferSize.X = cb->Info.dwSize.X;
2328 cb->BufferSize.Y = cb->Info.dwSize.Y;
2329 NumCells = cb->BufferSize.X * cb->BufferSize.Y;
2330 vim_free(cb->Buffer);
2331 cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
2332 if (cb->Buffer == NULL)
2333 return FALSE;
2334 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002335
2336 /*
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002337 * We will now copy the console screen buffer into our buffer.
2338 * ReadConsoleOutput() seems to be limited as far as how much you
2339 * can read at a time. Empirically, this number seems to be about
2340 * 12000 cells (rows * columns). Start at position (0, 0) and copy
2341 * in chunks until it is all copied. The chunks will all have the
2342 * same horizontal characteristics, so initialize them now. The
2343 * height of each chunk will be (12000 / width).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002344 */
Bram Moolenaar61594242015-09-01 20:23:37 +02002345 BufferCoord.X = 0;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002346 ReadRegion.Left = 0;
2347 ReadRegion.Right = cb->Info.dwSize.X - 1;
2348 Y_incr = 12000 / cb->Info.dwSize.X;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002349
2350 numregions = (cb->Info.dwSize.Y + Y_incr - 1) / Y_incr;
2351 if (cb->Regions == NULL || numregions != cb->NumRegions)
2352 {
2353 cb->NumRegions = numregions;
2354 vim_free(cb->Regions);
2355 cb->Regions = (PSMALL_RECT)alloc(cb->NumRegions * sizeof(SMALL_RECT));
2356 if (cb->Regions == NULL)
2357 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01002358 VIM_CLEAR(cb->Buffer);
Bram Moolenaar444fda22017-08-11 20:37:00 +02002359 return FALSE;
2360 }
2361 }
2362
2363 for (i = 0, Y = 0; i < cb->NumRegions; i++, Y += Y_incr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002364 {
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002365 /*
2366 * Read into position (0, Y) in our buffer.
2367 */
2368 BufferCoord.Y = Y;
2369 /*
2370 * Read the region whose top left corner is (0, Y) and whose bottom
2371 * right corner is (width - 1, Y + Y_incr - 1). This should define
2372 * a region of size width by Y_incr. Don't worry if this region is
2373 * too large for the remaining buffer; it will be cropped.
2374 */
2375 ReadRegion.Top = Y;
2376 ReadRegion.Bottom = Y + Y_incr - 1;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002377 if (!ReadConsoleOutputW(g_hConOut, /* output handle */
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002378 cb->Buffer, /* our buffer */
2379 cb->BufferSize, /* dimensions of our buffer */
2380 BufferCoord, /* offset in our buffer */
2381 &ReadRegion)) /* region to save */
2382 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01002383 VIM_CLEAR(cb->Buffer);
2384 VIM_CLEAR(cb->Regions);
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002385 return FALSE;
2386 }
Bram Moolenaar444fda22017-08-11 20:37:00 +02002387 cb->Regions[i] = ReadRegion;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002388 }
2389
2390 return TRUE;
2391}
2392
2393/*
2394 * RestoreConsoleBuffer()
2395 * Description:
2396 * Restores important information about the console buffer, including the
2397 * actual buffer contents, if desired. The information to restore is in
2398 * the same format used by SaveConsoleBuffer().
2399 * Returns:
2400 * TRUE on success
2401 */
2402 static BOOL
2403RestoreConsoleBuffer(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002404 ConsoleBuffer *cb,
2405 BOOL RestoreScreen)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002406{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002407 COORD BufferCoord;
2408 SMALL_RECT WriteRegion;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002409 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002410
2411 if (cb == NULL || !cb->IsValid)
2412 return FALSE;
2413
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002414 /*
2415 * Before restoring the buffer contents, clear the current buffer, and
2416 * restore the cursor position and window information. Doing this now
2417 * prevents old buffer contents from "flashing" onto the screen.
2418 */
2419 if (RestoreScreen)
2420 ClearConsoleBuffer(cb->Info.wAttributes);
2421
2422 FitConsoleWindow(cb->Info.dwSize, TRUE);
2423 if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
2424 return FALSE;
2425 if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
2426 return FALSE;
2427
2428 if (!RestoreScreen)
2429 {
2430 /*
2431 * No need to restore the screen buffer contents, so we're done.
2432 */
2433 return TRUE;
2434 }
2435
2436 if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
2437 return FALSE;
2438 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
2439 return FALSE;
2440
2441 /*
2442 * Restore the screen buffer contents.
2443 */
2444 if (cb->Buffer != NULL)
2445 {
Bram Moolenaar444fda22017-08-11 20:37:00 +02002446 for (i = 0; i < cb->NumRegions; i++)
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002447 {
Bram Moolenaar444fda22017-08-11 20:37:00 +02002448 BufferCoord.X = cb->Regions[i].Left;
2449 BufferCoord.Y = cb->Regions[i].Top;
2450 WriteRegion = cb->Regions[i];
2451 if (!WriteConsoleOutputW(g_hConOut, /* output handle */
2452 cb->Buffer, /* our buffer */
2453 cb->BufferSize, /* dimensions of our buffer */
2454 BufferCoord, /* offset in our buffer */
2455 &WriteRegion)) /* region to restore */
2456 {
2457 return FALSE;
2458 }
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002459 }
2460 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002461
2462 return TRUE;
2463}
2464
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002465#define FEAT_RESTORE_ORIG_SCREEN
2466#ifdef FEAT_RESTORE_ORIG_SCREEN
2467static ConsoleBuffer g_cbOrig = { 0 };
2468#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002469static ConsoleBuffer g_cbNonTermcap = { 0 };
2470static ConsoleBuffer g_cbTermcap = { 0 };
2471
2472#ifdef FEAT_TITLE
2473#ifdef __BORLANDC__
2474typedef HWND (__stdcall *GETCONSOLEWINDOWPROC)(VOID);
2475#else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002476typedef HWND (WINAPI *GETCONSOLEWINDOWPROC)(VOID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002477#endif
2478char g_szOrigTitle[256] = { 0 };
2479HWND g_hWnd = NULL; /* also used in os_mswin.c */
2480static HICON g_hOrigIconSmall = NULL;
2481static HICON g_hOrigIcon = NULL;
2482static HICON g_hVimIcon = NULL;
2483static BOOL g_fCanChangeIcon = FALSE;
2484
2485/* ICON* are not defined in VC++ 4.0 */
2486#ifndef ICON_SMALL
2487#define ICON_SMALL 0
2488#endif
2489#ifndef ICON_BIG
2490#define ICON_BIG 1
2491#endif
2492/*
2493 * GetConsoleIcon()
2494 * Description:
2495 * Attempts to retrieve the small icon and/or the big icon currently in
2496 * use by a given window.
2497 * Returns:
2498 * TRUE on success
2499 */
2500 static BOOL
2501GetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002502 HWND hWnd,
2503 HICON *phIconSmall,
2504 HICON *phIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002505{
2506 if (hWnd == NULL)
2507 return FALSE;
2508
2509 if (phIconSmall != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002510 *phIconSmall = (HICON)SendMessage(hWnd, WM_GETICON,
2511 (WPARAM)ICON_SMALL, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002512 if (phIcon != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002513 *phIcon = (HICON)SendMessage(hWnd, WM_GETICON,
2514 (WPARAM)ICON_BIG, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002515 return TRUE;
2516}
2517
2518/*
2519 * SetConsoleIcon()
2520 * Description:
2521 * Attempts to change the small icon and/or the big icon currently in
2522 * use by a given window.
2523 * Returns:
2524 * TRUE on success
2525 */
2526 static BOOL
2527SetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002528 HWND hWnd,
2529 HICON hIconSmall,
2530 HICON hIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002531{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002532 if (hWnd == NULL)
2533 return FALSE;
2534
2535 if (hIconSmall != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002536 SendMessage(hWnd, WM_SETICON,
2537 (WPARAM)ICON_SMALL, (LPARAM)hIconSmall);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002538 if (hIcon != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002539 SendMessage(hWnd, WM_SETICON,
2540 (WPARAM)ICON_BIG, (LPARAM) hIcon);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002541 return TRUE;
2542}
2543
2544/*
2545 * SaveConsoleTitleAndIcon()
2546 * Description:
2547 * Saves the current console window title in g_szOrigTitle, for later
2548 * restoration. Also, attempts to obtain a handle to the console window,
2549 * and use it to save the small and big icons currently in use by the
2550 * console window. This is not always possible on some versions of Windows;
2551 * nor is it possible when running Vim remotely using Telnet (since the
2552 * console window the user sees is owned by a remote process).
2553 */
2554 static void
2555SaveConsoleTitleAndIcon(void)
2556{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002557 /* Save the original title. */
2558 if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle)))
2559 return;
2560
2561 /*
2562 * Obtain a handle to the console window using GetConsoleWindow() from
2563 * KERNEL32.DLL; we need to handle in order to change the window icon.
2564 * This function only exists on NT-based Windows, starting with Windows
2565 * 2000. On older operating systems, we can't change the window icon
2566 * anyway.
2567 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002568 g_hWnd = GetConsoleWindow();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002569 if (g_hWnd == NULL)
2570 return;
2571
2572 /* Save the original console window icon. */
2573 GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon);
2574 if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL)
2575 return;
2576
2577 /* Extract the first icon contained in the Vim executable. */
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02002578 if (mch_icon_load((HANDLE *)&g_hVimIcon) == FAIL || g_hVimIcon == NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002579 g_hVimIcon = ExtractIcon(NULL, (LPCSTR)exe_name, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002580 if (g_hVimIcon != NULL)
2581 g_fCanChangeIcon = TRUE;
2582}
2583#endif
2584
2585static int g_fWindInitCalled = FALSE;
2586static int g_fTermcapMode = FALSE;
2587static CONSOLE_CURSOR_INFO g_cci;
2588static DWORD g_cmodein = 0;
2589static DWORD g_cmodeout = 0;
2590
2591/*
2592 * non-GUI version of mch_init().
2593 */
2594 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002595mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002596{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002597#ifndef FEAT_RESTORE_ORIG_SCREEN
2598 CONSOLE_SCREEN_BUFFER_INFO csbi;
2599#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002600#ifndef __MINGW32__
2601 extern int _fmode;
2602#endif
2603
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002604 /* Silently handle invalid parameters to CRT functions */
2605 SET_INVALID_PARAM_HANDLER;
2606
Bram Moolenaar071d4272004-06-13 20:20:40 +00002607 /* Let critical errors result in a failure, not in a dialog box. Required
2608 * for the timestamp test to work on removed floppies. */
2609 SetErrorMode(SEM_FAILCRITICALERRORS);
2610
2611 _fmode = O_BINARY; /* we do our own CR-LF translation */
2612 out_flush();
2613
2614 /* Obtain handles for the standard Console I/O devices */
2615 if (read_cmd_fd == 0)
2616 g_hConIn = GetStdHandle(STD_INPUT_HANDLE);
2617 else
2618 create_conin();
2619 g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
2620
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002621#ifdef FEAT_RESTORE_ORIG_SCREEN
2622 /* Save the initial console buffer for later restoration */
2623 SaveConsoleBuffer(&g_cbOrig);
2624 g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
2625#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002626 /* Get current text attributes */
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002627 GetConsoleScreenBufferInfo(g_hConOut, &csbi);
2628 g_attrCurrent = g_attrDefault = csbi.wAttributes;
2629#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002630 if (cterm_normal_fg_color == 0)
2631 cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
2632 if (cterm_normal_bg_color == 0)
2633 cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
2634
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02002635 // Fg and Bg color index nunmber at startup
2636 g_color_index_fg = g_attrDefault & 0xf;
2637 g_color_index_bg = (g_attrDefault >> 4) & 0xf;
2638
Bram Moolenaar071d4272004-06-13 20:20:40 +00002639 /* set termcap codes to current text attributes */
2640 update_tcap(g_attrCurrent);
2641
2642 GetConsoleCursorInfo(g_hConOut, &g_cci);
2643 GetConsoleMode(g_hConIn, &g_cmodein);
2644 GetConsoleMode(g_hConOut, &g_cmodeout);
2645
2646#ifdef FEAT_TITLE
2647 SaveConsoleTitleAndIcon();
2648 /*
2649 * Set both the small and big icons of the console window to Vim's icon.
2650 * Note that Vim presently only has one size of icon (32x32), but it
2651 * automatically gets scaled down to 16x16 when setting the small icon.
2652 */
2653 if (g_fCanChangeIcon)
2654 SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon);
2655#endif
2656
2657 ui_get_shellsize();
2658
2659#ifdef MCH_WRITE_DUMP
2660 fdDump = fopen("dump", "wt");
2661
2662 if (fdDump)
2663 {
2664 time_t t;
2665
2666 time(&t);
2667 fputs(ctime(&t), fdDump);
2668 fflush(fdDump);
2669 }
2670#endif
2671
2672 g_fWindInitCalled = TRUE;
2673
2674#ifdef FEAT_MOUSE
2675 g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
2676#endif
2677
2678#ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002679 win_clip_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002680#endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01002681
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01002682 vtp_flag_init();
Bram Moolenaarcafafb32018-02-22 21:07:09 +01002683 vtp_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002684}
2685
2686/*
2687 * non-GUI version of mch_exit().
2688 * Shut down and exit with status `r'
2689 * Careful: mch_exit() may be called before mch_init()!
2690 */
2691 void
2692mch_exit(int r)
2693{
Bram Moolenaar955f1982017-02-05 15:10:51 +01002694 exiting = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002695
Bram Moolenaarcafafb32018-02-22 21:07:09 +01002696 vtp_exit();
2697
Bram Moolenaar955f1982017-02-05 15:10:51 +01002698 stoptermcap();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002699 if (g_fWindInitCalled)
2700 settmode(TMODE_COOK);
2701
2702 ml_close_all(TRUE); /* remove all memfiles */
2703
2704 if (g_fWindInitCalled)
2705 {
2706#ifdef FEAT_TITLE
Bram Moolenaar40385db2018-08-07 22:31:44 +02002707 mch_restore_title(SAVE_RESTORE_BOTH);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002708 /*
2709 * Restore both the small and big icons of the console window to
2710 * what they were at startup. Don't do this when the window is
2711 * closed, Vim would hang here.
2712 */
2713 if (g_fCanChangeIcon && !g_fForceExit)
2714 SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon);
2715#endif
2716
2717#ifdef MCH_WRITE_DUMP
2718 if (fdDump)
2719 {
2720 time_t t;
2721
2722 time(&t);
2723 fputs(ctime(&t), fdDump);
2724 fclose(fdDump);
2725 }
2726 fdDump = NULL;
2727#endif
2728 }
2729
2730 SetConsoleCursorInfo(g_hConOut, &g_cci);
2731 SetConsoleMode(g_hConIn, g_cmodein);
2732 SetConsoleMode(g_hConOut, g_cmodeout);
2733
2734#ifdef DYNAMIC_GETTEXT
2735 dyn_libintl_end();
2736#endif
2737
2738 exit(r);
2739}
2740#endif /* !FEAT_GUI_W32 */
2741
Bram Moolenaar071d4272004-06-13 20:20:40 +00002742/*
2743 * Do we have an interactive window?
2744 */
2745 int
2746mch_check_win(
Bram Moolenaar1266d672017-02-01 13:43:36 +01002747 int argc UNUSED,
2748 char **argv UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002749{
2750 get_exe_name();
2751
2752#ifdef FEAT_GUI_W32
2753 return OK; /* GUI always has a tty */
2754#else
2755 if (isatty(1))
2756 return OK;
2757 return FAIL;
2758#endif
2759}
2760
2761
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002762/*
2763 * fname_casew(): Wide version of fname_case(). Set the case of the file name,
2764 * if it already exists. When "len" is > 0, also expand short to long
2765 * filenames.
2766 * Return FAIL if wide functions are not available, OK otherwise.
2767 * NOTE: much of this is identical to fname_case(), keep in sync!
2768 */
2769 static int
2770fname_casew(
2771 WCHAR *name,
2772 int len)
2773{
2774 WCHAR szTrueName[_MAX_PATH + 2];
2775 WCHAR szTrueNameTemp[_MAX_PATH + 2];
2776 WCHAR *ptrue, *ptruePrev;
2777 WCHAR *porig, *porigPrev;
2778 int flen;
2779 WIN32_FIND_DATAW fb;
Bram Moolenaar73c61632013-12-07 14:48:10 +01002780 HANDLE hFind = INVALID_HANDLE_VALUE;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002781 int c;
2782 int slen;
2783
2784 flen = (int)wcslen(name);
2785 if (flen > _MAX_PATH)
2786 return OK;
2787
2788 /* slash_adjust(name) not needed, already adjusted by fname_case(). */
2789
2790 /* Build the new name in szTrueName[] one component at a time. */
2791 porig = name;
2792 ptrue = szTrueName;
2793
2794 if (iswalpha(porig[0]) && porig[1] == L':')
2795 {
2796 /* copy leading drive letter */
2797 *ptrue++ = *porig++;
2798 *ptrue++ = *porig++;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002799 }
Bram Moolenaar73c61632013-12-07 14:48:10 +01002800 *ptrue = NUL; /* in case nothing follows */
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002801
2802 while (*porig != NUL)
2803 {
2804 /* copy \ characters */
2805 while (*porig == psepc)
2806 *ptrue++ = *porig++;
2807
2808 ptruePrev = ptrue;
2809 porigPrev = porig;
2810 while (*porig != NUL && *porig != psepc)
2811 {
2812 *ptrue++ = *porig++;
2813 }
2814 *ptrue = NUL;
2815
2816 /* To avoid a slow failure append "\*" when searching a directory,
2817 * server or network share. */
2818 wcscpy(szTrueNameTemp, szTrueName);
2819 slen = (int)wcslen(szTrueNameTemp);
2820 if (*porig == psepc && slen + 2 < _MAX_PATH)
2821 wcscpy(szTrueNameTemp + slen, L"\\*");
2822
2823 /* Skip "", "." and "..". */
2824 if (ptrue > ptruePrev
2825 && (ptruePrev[0] != L'.'
2826 || (ptruePrev[1] != NUL
2827 && (ptruePrev[1] != L'.' || ptruePrev[2] != NUL)))
2828 && (hFind = FindFirstFileW(szTrueNameTemp, &fb))
2829 != INVALID_HANDLE_VALUE)
2830 {
2831 c = *porig;
2832 *porig = NUL;
2833
2834 /* Only use the match when it's the same name (ignoring case) or
2835 * expansion is allowed and there is a match with the short name
2836 * and there is enough room. */
2837 if (_wcsicoll(porigPrev, fb.cFileName) == 0
2838 || (len > 0
2839 && (_wcsicoll(porigPrev, fb.cAlternateFileName) == 0
2840 && (int)(ptruePrev - szTrueName)
2841 + (int)wcslen(fb.cFileName) < len)))
2842 {
2843 wcscpy(ptruePrev, fb.cFileName);
2844
2845 /* Look for exact match and prefer it if found. Must be a
2846 * long name, otherwise there would be only one match. */
2847 while (FindNextFileW(hFind, &fb))
2848 {
2849 if (*fb.cAlternateFileName != NUL
2850 && (wcscoll(porigPrev, fb.cFileName) == 0
2851 || (len > 0
2852 && (_wcsicoll(porigPrev,
2853 fb.cAlternateFileName) == 0
2854 && (int)(ptruePrev - szTrueName)
2855 + (int)wcslen(fb.cFileName) < len))))
2856 {
2857 wcscpy(ptruePrev, fb.cFileName);
2858 break;
2859 }
2860 }
2861 }
2862 FindClose(hFind);
2863 *porig = c;
2864 ptrue = ptruePrev + wcslen(ptruePrev);
2865 }
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002866 }
2867
2868 wcscpy(name, szTrueName);
2869 return OK;
2870}
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002871
Bram Moolenaar071d4272004-06-13 20:20:40 +00002872/*
2873 * fname_case(): Set the case of the file name, if it already exists.
2874 * When "len" is > 0, also expand short to long filenames.
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002875 * NOTE: much of this is identical to fname_casew(), keep in sync!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002876 */
2877 void
2878fname_case(
2879 char_u *name,
2880 int len)
2881{
2882 char szTrueName[_MAX_PATH + 2];
Bram Moolenaar464c9252010-10-13 20:37:41 +02002883 char szTrueNameTemp[_MAX_PATH + 2];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002884 char *ptrue, *ptruePrev;
2885 char *porig, *porigPrev;
2886 int flen;
2887 WIN32_FIND_DATA fb;
2888 HANDLE hFind;
2889 int c;
Bram Moolenaar464c9252010-10-13 20:37:41 +02002890 int slen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002891
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +00002892 flen = (int)STRLEN(name);
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002893 if (flen == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002894 return;
2895
2896 slash_adjust(name);
2897
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002898 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2899 {
2900 WCHAR *p = enc_to_utf16(name, NULL);
2901
2902 if (p != NULL)
2903 {
2904 char_u *q;
Bram Moolenaar21d89b62014-10-07 10:38:40 +02002905 WCHAR buf[_MAX_PATH + 1];
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002906
Bram Moolenaar21d89b62014-10-07 10:38:40 +02002907 wcsncpy(buf, p, _MAX_PATH);
2908 buf[_MAX_PATH] = L'\0';
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002909 vim_free(p);
2910
2911 if (fname_casew(buf, (len > 0) ? _MAX_PATH : 0) == OK)
2912 {
2913 q = utf16_to_enc(buf, NULL);
2914 if (q != NULL)
2915 {
2916 vim_strncpy(name, q, (len > 0) ? len - 1 : flen);
2917 vim_free(q);
2918 return;
2919 }
2920 }
2921 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002922 return;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002923 }
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002924
2925 /* If 'enc' is utf-8, flen can be larger than _MAX_PATH.
2926 * So we should check this after calling wide function. */
2927 if (flen > _MAX_PATH)
2928 return;
2929
Bram Moolenaar071d4272004-06-13 20:20:40 +00002930 /* Build the new name in szTrueName[] one component at a time. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002931 porig = (char *)name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002932 ptrue = szTrueName;
2933
2934 if (isalpha(porig[0]) && porig[1] == ':')
2935 {
2936 /* copy leading drive letter */
2937 *ptrue++ = *porig++;
2938 *ptrue++ = *porig++;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002939 }
Bram Moolenaar73c61632013-12-07 14:48:10 +01002940 *ptrue = NUL; /* in case nothing follows */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002941
2942 while (*porig != NUL)
2943 {
2944 /* copy \ characters */
2945 while (*porig == psepc)
2946 *ptrue++ = *porig++;
2947
2948 ptruePrev = ptrue;
2949 porigPrev = porig;
2950 while (*porig != NUL && *porig != psepc)
2951 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002952 int l;
2953
2954 if (enc_dbcs)
2955 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002956 l = (*mb_ptr2len)((char_u *)porig);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002957 while (--l >= 0)
2958 *ptrue++ = *porig++;
2959 }
2960 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002961 *ptrue++ = *porig++;
2962 }
2963 *ptrue = NUL;
2964
Bram Moolenaar464c9252010-10-13 20:37:41 +02002965 /* To avoid a slow failure append "\*" when searching a directory,
2966 * server or network share. */
2967 STRCPY(szTrueNameTemp, szTrueName);
Bram Moolenaar6b5ef062010-10-27 12:18:00 +02002968 slen = (int)strlen(szTrueNameTemp);
Bram Moolenaar464c9252010-10-13 20:37:41 +02002969 if (*porig == psepc && slen + 2 < _MAX_PATH)
2970 STRCPY(szTrueNameTemp + slen, "\\*");
2971
Bram Moolenaar071d4272004-06-13 20:20:40 +00002972 /* Skip "", "." and "..". */
2973 if (ptrue > ptruePrev
2974 && (ptruePrev[0] != '.'
2975 || (ptruePrev[1] != NUL
2976 && (ptruePrev[1] != '.' || ptruePrev[2] != NUL)))
Bram Moolenaar464c9252010-10-13 20:37:41 +02002977 && (hFind = FindFirstFile(szTrueNameTemp, &fb))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002978 != INVALID_HANDLE_VALUE)
2979 {
2980 c = *porig;
2981 *porig = NUL;
2982
2983 /* Only use the match when it's the same name (ignoring case) or
2984 * expansion is allowed and there is a match with the short name
2985 * and there is enough room. */
2986 if (_stricoll(porigPrev, fb.cFileName) == 0
2987 || (len > 0
2988 && (_stricoll(porigPrev, fb.cAlternateFileName) == 0
2989 && (int)(ptruePrev - szTrueName)
2990 + (int)strlen(fb.cFileName) < len)))
2991 {
2992 STRCPY(ptruePrev, fb.cFileName);
2993
2994 /* Look for exact match and prefer it if found. Must be a
2995 * long name, otherwise there would be only one match. */
2996 while (FindNextFile(hFind, &fb))
2997 {
2998 if (*fb.cAlternateFileName != NUL
2999 && (strcoll(porigPrev, fb.cFileName) == 0
3000 || (len > 0
3001 && (_stricoll(porigPrev,
3002 fb.cAlternateFileName) == 0
3003 && (int)(ptruePrev - szTrueName)
3004 + (int)strlen(fb.cFileName) < len))))
3005 {
3006 STRCPY(ptruePrev, fb.cFileName);
3007 break;
3008 }
3009 }
3010 }
3011 FindClose(hFind);
3012 *porig = c;
3013 ptrue = ptruePrev + strlen(ptruePrev);
3014 }
3015 }
3016
3017 STRCPY(name, szTrueName);
3018}
3019
3020
3021/*
3022 * Insert user name in s[len].
3023 */
3024 int
3025mch_get_user_name(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003026 char_u *s,
3027 int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003028{
Bram Moolenaar41a09032007-10-01 18:34:34 +00003029 char szUserName[256 + 1]; /* UNLEN is 256 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003030 DWORD cch = sizeof szUserName;
3031
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01003032 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3033 {
3034 WCHAR wszUserName[256 + 1]; /* UNLEN is 256 */
3035 DWORD wcch = sizeof(wszUserName) / sizeof(WCHAR);
3036
3037 if (GetUserNameW(wszUserName, &wcch))
3038 {
3039 char_u *p = utf16_to_enc(wszUserName, NULL);
3040
3041 if (p != NULL)
3042 {
3043 vim_strncpy(s, p, len - 1);
3044 vim_free(p);
3045 return OK;
3046 }
3047 }
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01003048 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003049 if (GetUserName(szUserName, &cch))
3050 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003051 vim_strncpy(s, (char_u *)szUserName, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003052 return OK;
3053 }
3054 s[0] = NUL;
3055 return FAIL;
3056}
3057
3058
3059/*
3060 * Insert host name in s[len].
3061 */
3062 void
3063mch_get_host_name(
3064 char_u *s,
3065 int len)
3066{
3067 DWORD cch = len;
3068
Bram Moolenaar2cc87382013-12-11 18:21:45 +01003069 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3070 {
3071 WCHAR wszHostName[256 + 1];
3072 DWORD wcch = sizeof(wszHostName) / sizeof(WCHAR);
3073
3074 if (GetComputerNameW(wszHostName, &wcch))
3075 {
3076 char_u *p = utf16_to_enc(wszHostName, NULL);
3077
3078 if (p != NULL)
3079 {
3080 vim_strncpy(s, p, len - 1);
3081 vim_free(p);
3082 return;
3083 }
3084 }
Bram Moolenaar2cc87382013-12-11 18:21:45 +01003085 }
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003086 if (!GetComputerName((LPSTR)s, &cch))
3087 vim_strncpy(s, (char_u *)"PC (Win32 Vim)", len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003088}
3089
3090
3091/*
3092 * return process ID
3093 */
3094 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003095mch_get_pid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003096{
3097 return (long)GetCurrentProcessId();
3098}
3099
3100
3101/*
3102 * Get name of current directory into buffer 'buf' of length 'len' bytes.
3103 * Return OK for success, FAIL for failure.
3104 */
3105 int
3106mch_dirname(
3107 char_u *buf,
3108 int len)
3109{
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003110 char_u abuf[_MAX_PATH + 1];
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003111 DWORD lfnlen;
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003112
Bram Moolenaar071d4272004-06-13 20:20:40 +00003113 /*
3114 * Originally this was:
3115 * return (getcwd(buf, len) != NULL ? OK : FAIL);
3116 * But the Win32s known bug list says that getcwd() doesn't work
3117 * so use the Win32 system call instead. <Negri>
3118 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003119 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3120 {
3121 WCHAR wbuf[_MAX_PATH + 1];
3122
3123 if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0)
3124 {
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003125 WCHAR wcbuf[_MAX_PATH + 1];
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003126 char_u *p = NULL;
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003127
3128 if (GetLongPathNameW(wbuf, wcbuf, _MAX_PATH) != 0)
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003129 {
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003130 p = utf16_to_enc(wcbuf, NULL);
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003131 if (STRLEN(p) >= (size_t)len)
3132 {
3133 // long path name is too long, fall back to short one
3134 vim_free(p);
3135 p = NULL;
3136 }
3137 }
3138 if (p == NULL)
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003139 p = utf16_to_enc(wbuf, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003140
3141 if (p != NULL)
3142 {
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00003143 vim_strncpy(buf, p, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003144 vim_free(p);
3145 return OK;
3146 }
3147 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003148 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003149 }
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003150 if (GetCurrentDirectory(len, (LPSTR)buf) == 0)
3151 return FAIL;
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003152 lfnlen = GetLongPathNameA((LPCSTR)buf, (LPSTR)abuf, _MAX_PATH);
3153 if (lfnlen == 0 || lfnlen >= (DWORD)len)
3154 // Failed to get long path name or it's too long: fall back to the
3155 // short path name.
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003156 return OK;
3157
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003158 STRCPY(buf, abuf);
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003159 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003160}
3161
3162/*
Bram Moolenaarffa22202013-11-21 12:34:11 +01003163 * Get file permissions for "name".
3164 * Return mode_t or -1 for error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003165 */
3166 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003167mch_getperm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003168{
Bram Moolenaar8767f522016-07-01 17:17:39 +02003169 stat_T st;
Bram Moolenaarffa22202013-11-21 12:34:11 +01003170 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003171
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003172 n = mch_stat((char *)name, &st);
Bram Moolenaar78cf3f02014-01-10 18:16:07 +01003173 return n == 0 ? (long)(unsigned short)st.st_mode : -1L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003174}
3175
3176
3177/*
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003178 * Set file permission for "name" to "perm".
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003179 *
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003180 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003181 */
3182 int
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003183mch_setperm(char_u *name, long perm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003184{
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003185 long n = -1;
3186
Bram Moolenaar071d4272004-06-13 20:20:40 +00003187 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3188 {
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003189 WCHAR *p = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003190
3191 if (p != NULL)
3192 {
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003193 n = _wchmod(p, perm);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003194 vim_free(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003195 if (n == -1)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003196 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003197 }
3198 }
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003199 if (n == -1)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003200 n = _chmod((const char *)name, perm);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003201 if (n == -1)
3202 return FAIL;
3203
3204 win32_set_archive(name);
3205
3206 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003207}
3208
3209/*
3210 * Set hidden flag for "name".
3211 */
3212 void
3213mch_hide(char_u *name)
3214{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003215 int attrs = win32_getattrs(name);
3216 if (attrs == -1)
3217 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003218
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003219 attrs |= FILE_ATTRIBUTE_HIDDEN;
3220 win32_setattrs(name, attrs);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003221}
3222
3223/*
Bram Moolenaar8a52ba72015-11-02 14:45:56 +01003224 * Return TRUE if file "name" exists and is hidden.
3225 */
3226 int
3227mch_ishidden(char_u *name)
3228{
3229 int f = win32_getattrs(name);
3230
3231 if (f == -1)
3232 return FALSE; /* file does not exist at all */
3233
3234 return (f & FILE_ATTRIBUTE_HIDDEN) != 0;
3235}
3236
3237/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003238 * return TRUE if "name" is a directory
3239 * return FALSE if "name" is not a directory or upon error
3240 */
3241 int
3242mch_isdir(char_u *name)
3243{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003244 int f = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003245
3246 if (f == -1)
3247 return FALSE; /* file does not exist at all */
3248
3249 return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
3250}
3251
3252/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003253 * return TRUE if "name" is a directory, NOT a symlink to a directory
3254 * return FALSE if "name" is not a directory
3255 * return FALSE for error
3256 */
3257 int
3258mch_isrealdir(char_u *name)
3259{
3260 return mch_isdir(name) && !mch_is_symbolic_link(name);
3261}
3262
3263/*
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003264 * Create directory "name".
3265 * Return 0 on success, -1 on error.
3266 */
3267 int
3268mch_mkdir(char_u *name)
3269{
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003270 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3271 {
3272 WCHAR *p;
3273 int retval;
3274
3275 p = enc_to_utf16(name, NULL);
3276 if (p == NULL)
3277 return -1;
3278 retval = _wmkdir(p);
3279 vim_free(p);
3280 return retval;
3281 }
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003282 return _mkdir((const char *)name);
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003283}
3284
3285/*
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003286 * Delete directory "name".
3287 * Return 0 on success, -1 on error.
3288 */
3289 int
3290mch_rmdir(char_u *name)
3291{
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003292 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3293 {
3294 WCHAR *p;
3295 int retval;
3296
3297 p = enc_to_utf16(name, NULL);
3298 if (p == NULL)
3299 return -1;
3300 retval = _wrmdir(p);
3301 vim_free(p);
3302 return retval;
3303 }
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003304 return _rmdir((const char *)name);
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003305}
3306
3307/*
Bram Moolenaar03f48552006-02-28 23:52:23 +00003308 * Return TRUE if file "fname" has more than one link.
3309 */
3310 int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003311mch_is_hard_link(char_u *fname)
Bram Moolenaar03f48552006-02-28 23:52:23 +00003312{
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003313 BY_HANDLE_FILE_INFORMATION info;
3314
3315 return win32_fileinfo(fname, &info) == FILEINFO_OK
3316 && info.nNumberOfLinks > 1;
3317}
3318
3319/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003320 * Return TRUE if "name" is a symbolic link (or a junction).
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003321 */
3322 int
Bram Moolenaar203258c2016-01-17 22:15:16 +01003323mch_is_symbolic_link(char_u *name)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003324{
3325 HANDLE hFind;
3326 int res = FALSE;
3327 WIN32_FIND_DATAA findDataA;
3328 DWORD fileFlags = 0, reparseTag = 0;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003329 WCHAR *wn = NULL;
3330 WIN32_FIND_DATAW findDataW;
3331
3332 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar203258c2016-01-17 22:15:16 +01003333 wn = enc_to_utf16(name, NULL);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003334 if (wn != NULL)
3335 {
3336 hFind = FindFirstFileW(wn, &findDataW);
3337 vim_free(wn);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003338 if (hFind != INVALID_HANDLE_VALUE)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003339 {
3340 fileFlags = findDataW.dwFileAttributes;
3341 reparseTag = findDataW.dwReserved0;
3342 }
3343 }
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003344 else
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003345 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003346 hFind = FindFirstFile((LPCSTR)name, &findDataA);
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003347 if (hFind != INVALID_HANDLE_VALUE)
3348 {
3349 fileFlags = findDataA.dwFileAttributes;
3350 reparseTag = findDataA.dwReserved0;
3351 }
3352 }
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003353
3354 if (hFind != INVALID_HANDLE_VALUE)
3355 FindClose(hFind);
3356
3357 if ((fileFlags & FILE_ATTRIBUTE_REPARSE_POINT)
Bram Moolenaar203258c2016-01-17 22:15:16 +01003358 && (reparseTag == IO_REPARSE_TAG_SYMLINK
3359 || reparseTag == IO_REPARSE_TAG_MOUNT_POINT))
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003360 res = TRUE;
3361
3362 return res;
3363}
3364
3365/*
3366 * Return TRUE if file "fname" has more than one link or if it is a symbolic
3367 * link.
3368 */
3369 int
3370mch_is_linked(char_u *fname)
3371{
3372 if (mch_is_hard_link(fname) || mch_is_symbolic_link(fname))
3373 return TRUE;
3374 return FALSE;
3375}
3376
3377/*
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003378 * Get the by-handle-file-information for "fname".
3379 * Returns FILEINFO_OK when OK.
3380 * returns FILEINFO_ENC_FAIL when enc_to_utf16() failed.
3381 * Returns FILEINFO_READ_FAIL when CreateFile() failed.
3382 * Returns FILEINFO_INFO_FAIL when GetFileInformationByHandle() failed.
3383 */
3384 int
3385win32_fileinfo(char_u *fname, BY_HANDLE_FILE_INFORMATION *info)
3386{
Bram Moolenaar03f48552006-02-28 23:52:23 +00003387 HANDLE hFile;
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003388 int res = FILEINFO_READ_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003389 WCHAR *wn = NULL;
3390
3391 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003392 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00003393 wn = enc_to_utf16(fname, NULL);
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003394 if (wn == NULL)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003395 return FILEINFO_ENC_FAIL;
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003396 }
Bram Moolenaar03f48552006-02-28 23:52:23 +00003397 if (wn != NULL)
3398 {
3399 hFile = CreateFileW(wn, /* file name */
3400 GENERIC_READ, /* access mode */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003401 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003402 NULL, /* security descriptor */
3403 OPEN_EXISTING, /* creation disposition */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003404 FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003405 NULL); /* handle to template file */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003406 vim_free(wn);
Bram Moolenaar03f48552006-02-28 23:52:23 +00003407 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003408 else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003409 hFile = CreateFile((LPCSTR)fname, /* file name */
3410 GENERIC_READ, /* access mode */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003411 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003412 NULL, /* security descriptor */
3413 OPEN_EXISTING, /* creation disposition */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003414 FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003415 NULL); /* handle to template file */
3416
3417 if (hFile != INVALID_HANDLE_VALUE)
3418 {
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003419 if (GetFileInformationByHandle(hFile, info) != 0)
3420 res = FILEINFO_OK;
3421 else
3422 res = FILEINFO_INFO_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003423 CloseHandle(hFile);
3424 }
3425
Bram Moolenaar03f48552006-02-28 23:52:23 +00003426 return res;
3427}
3428
3429/*
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003430 * get file attributes for `name'
3431 * -1 : error
3432 * else FILE_ATTRIBUTE_* defined in winnt.h
3433 */
Bram Moolenaarffa22202013-11-21 12:34:11 +01003434 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003435win32_getattrs(char_u *name)
3436{
3437 int attr;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003438 WCHAR *p = NULL;
3439
3440 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3441 p = enc_to_utf16(name, NULL);
3442
3443 if (p != NULL)
3444 {
3445 attr = GetFileAttributesW(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003446 vim_free(p);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003447 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003448 else
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003449 attr = GetFileAttributes((char *)name);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003450
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003451 return attr;
3452}
3453
3454/*
3455 * set file attributes for `name' to `attrs'
3456 *
3457 * return -1 for failure, 0 otherwise
3458 */
Bram Moolenaar6dff58f2018-09-30 21:43:26 +02003459 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003460win32_setattrs(char_u *name, int attrs)
3461{
3462 int res;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003463 WCHAR *p = NULL;
3464
3465 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3466 p = enc_to_utf16(name, NULL);
3467
3468 if (p != NULL)
3469 {
3470 res = SetFileAttributesW(p, attrs);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003471 vim_free(p);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003472 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003473 else
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003474 res = SetFileAttributes((char *)name, attrs);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003475
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003476 return res ? 0 : -1;
3477}
3478
3479/*
3480 * Set archive flag for "name".
3481 */
Bram Moolenaar6dff58f2018-09-30 21:43:26 +02003482 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003483win32_set_archive(char_u *name)
3484{
3485 int attrs = win32_getattrs(name);
3486 if (attrs == -1)
3487 return -1;
3488
3489 attrs |= FILE_ATTRIBUTE_ARCHIVE;
3490 return win32_setattrs(name, attrs);
3491}
3492
3493/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003494 * Return TRUE if file or directory "name" is writable (not readonly).
3495 * Strange semantics of Win32: a readonly directory is writable, but you can't
3496 * delete a file. Let's say this means it is writable.
3497 */
3498 int
3499mch_writable(char_u *name)
3500{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003501 int attrs = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003502
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003503 return (attrs != -1 && (!(attrs & FILE_ATTRIBUTE_READONLY)
3504 || (attrs & FILE_ATTRIBUTE_DIRECTORY)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003505}
3506
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507/*
Bram Moolenaar43663192017-03-05 14:29:12 +01003508 * Return TRUE if "name" can be executed, FALSE if not.
Bram Moolenaar77b77102015-03-21 22:18:41 +01003509 * If "use_path" is FALSE only check if "name" is executable.
Bram Moolenaar43663192017-03-05 14:29:12 +01003510 * When returning TRUE and "path" is not NULL save the path and set "*path" to
3511 * the allocated memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512 */
3513 int
Bram Moolenaar77b77102015-03-21 22:18:41 +01003514mch_can_exe(char_u *name, char_u **path, int use_path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003515{
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003516 char_u buf[_MAX_PATH];
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003517 int len = (int)STRLEN(name);
Bram Moolenaar82956662018-10-06 15:18:45 +02003518 char_u *p, *saved;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003519
3520 if (len >= _MAX_PATH) /* safety check */
3521 return FALSE;
3522
Bram Moolenaar82956662018-10-06 15:18:45 +02003523 /* Ty using the name directly when a Unix-shell like 'shell'. */
3524 if (strstr((char *)gettail(p_sh), "sh") != NULL)
Bram Moolenaar43663192017-03-05 14:29:12 +01003525 if (executable_exists((char *)name, path, use_path))
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003526 return TRUE;
3527
3528 /*
3529 * Loop over all extensions in $PATHEXT.
3530 */
Bram Moolenaar82956662018-10-06 15:18:45 +02003531 p = mch_getenv("PATHEXT");
3532 if (p == NULL)
3533 p = (char_u *)".com;.exe;.bat;.cmd";
3534 saved = vim_strsave(p);
3535 if (saved == NULL)
3536 return FALSE;
3537 p = saved;
3538 while (*p)
3539 {
3540 char_u *tmp = vim_strchr(p, ';');
3541
3542 if (tmp != NULL)
3543 *tmp = NUL;
3544 if (_stricoll((char *)name + len - STRLEN(p), (char *)p) == 0
3545 && executable_exists((char *)name, path, use_path))
3546 {
3547 vim_free(saved);
3548 return TRUE;
3549 }
3550 if (tmp == NULL)
3551 break;
3552 p = tmp + 1;
3553 }
3554 vim_free(saved);
3555
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00003556 vim_strncpy(buf, name, _MAX_PATH - 1);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003557 p = mch_getenv("PATHEXT");
3558 if (p == NULL)
3559 p = (char_u *)".com;.exe;.bat;.cmd";
3560 while (*p)
3561 {
3562 if (p[0] == '.' && (p[1] == NUL || p[1] == ';'))
3563 {
3564 /* A single "." means no extension is added. */
3565 buf[len] = NUL;
3566 ++p;
3567 if (*p)
3568 ++p;
3569 }
3570 else
3571 copy_option_part(&p, buf + len, _MAX_PATH - len, ";");
Bram Moolenaar43663192017-03-05 14:29:12 +01003572 if (executable_exists((char *)buf, path, use_path))
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003573 return TRUE;
3574 }
3575 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003576}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003577
3578/*
3579 * Check what "name" is:
3580 * NODE_NORMAL: file or directory (or doesn't exist)
3581 * NODE_WRITABLE: writable device, socket, fifo, etc.
3582 * NODE_OTHER: non-writable things
3583 */
3584 int
3585mch_nodetype(char_u *name)
3586{
3587 HANDLE hFile;
3588 int type;
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003589 WCHAR *wn = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003590
Bram Moolenaar043545e2006-10-10 16:44:07 +00003591 /* We can't open a file with a name "\\.\con" or "\\.\prn" and trying to
3592 * read from it later will cause Vim to hang. Thus return NODE_WRITABLE
3593 * here. */
3594 if (STRNCMP(name, "\\\\.\\", 4) == 0)
3595 return NODE_WRITABLE;
3596
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003597 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003598 wn = enc_to_utf16(name, NULL);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003599
3600 if (wn != NULL)
3601 {
3602 hFile = CreateFileW(wn, /* file name */
3603 GENERIC_WRITE, /* access mode */
3604 0, /* share mode */
3605 NULL, /* security descriptor */
3606 OPEN_EXISTING, /* creation disposition */
3607 0, /* file attributes */
3608 NULL); /* handle to template file */
3609 vim_free(wn);
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003610 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003611 else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003612 hFile = CreateFile((LPCSTR)name, /* file name */
3613 GENERIC_WRITE, /* access mode */
3614 0, /* share mode */
3615 NULL, /* security descriptor */
3616 OPEN_EXISTING, /* creation disposition */
3617 0, /* file attributes */
3618 NULL); /* handle to template file */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003619
3620 if (hFile == INVALID_HANDLE_VALUE)
3621 return NODE_NORMAL;
3622
3623 type = GetFileType(hFile);
3624 CloseHandle(hFile);
3625 if (type == FILE_TYPE_CHAR)
3626 return NODE_WRITABLE;
3627 if (type == FILE_TYPE_DISK)
3628 return NODE_NORMAL;
3629 return NODE_OTHER;
3630}
3631
3632#ifdef HAVE_ACL
3633struct my_acl
3634{
3635 PSECURITY_DESCRIPTOR pSecurityDescriptor;
3636 PSID pSidOwner;
3637 PSID pSidGroup;
3638 PACL pDacl;
3639 PACL pSacl;
3640};
3641#endif
3642
3643/*
3644 * Return a pointer to the ACL of file "fname" in allocated memory.
3645 * Return NULL if the ACL is not available for whatever reason.
3646 */
3647 vim_acl_T
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003648mch_get_acl(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003649{
3650#ifndef HAVE_ACL
3651 return (vim_acl_T)NULL;
3652#else
3653 struct my_acl *p = NULL;
Bram Moolenaar27515922013-06-29 15:36:26 +02003654 DWORD err;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003655
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003656 p = (struct my_acl *)alloc_clear((unsigned)sizeof(struct my_acl));
3657 if (p != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003659 WCHAR *wn = NULL;
Bram Moolenaar27515922013-06-29 15:36:26 +02003660
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003661 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3662 wn = enc_to_utf16(fname, NULL);
3663 if (wn != NULL)
3664 {
3665 /* Try to retrieve the entire security descriptor. */
3666 err = GetNamedSecurityInfoW(
3667 wn, // Abstract filename
3668 SE_FILE_OBJECT, // File Object
3669 OWNER_SECURITY_INFORMATION |
3670 GROUP_SECURITY_INFORMATION |
3671 DACL_SECURITY_INFORMATION |
3672 SACL_SECURITY_INFORMATION,
3673 &p->pSidOwner, // Ownership information.
3674 &p->pSidGroup, // Group membership.
3675 &p->pDacl, // Discretionary information.
3676 &p->pSacl, // For auditing purposes.
3677 &p->pSecurityDescriptor);
3678 if (err == ERROR_ACCESS_DENIED ||
3679 err == ERROR_PRIVILEGE_NOT_HELD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003680 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003681 /* Retrieve only DACL. */
3682 (void)GetNamedSecurityInfoW(
3683 wn,
3684 SE_FILE_OBJECT,
3685 DACL_SECURITY_INFORMATION,
3686 NULL,
3687 NULL,
3688 &p->pDacl,
3689 NULL,
3690 &p->pSecurityDescriptor);
Bram Moolenaar27515922013-06-29 15:36:26 +02003691 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003692 if (p->pSecurityDescriptor == NULL)
Bram Moolenaar27515922013-06-29 15:36:26 +02003693 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003694 mch_free_acl((vim_acl_T)p);
3695 p = NULL;
3696 }
3697 vim_free(wn);
3698 }
3699 else
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003700 {
3701 /* Try to retrieve the entire security descriptor. */
3702 err = GetNamedSecurityInfo(
3703 (LPSTR)fname, // Abstract filename
3704 SE_FILE_OBJECT, // File Object
3705 OWNER_SECURITY_INFORMATION |
3706 GROUP_SECURITY_INFORMATION |
3707 DACL_SECURITY_INFORMATION |
3708 SACL_SECURITY_INFORMATION,
3709 &p->pSidOwner, // Ownership information.
3710 &p->pSidGroup, // Group membership.
3711 &p->pDacl, // Discretionary information.
3712 &p->pSacl, // For auditing purposes.
3713 &p->pSecurityDescriptor);
3714 if (err == ERROR_ACCESS_DENIED ||
3715 err == ERROR_PRIVILEGE_NOT_HELD)
3716 {
3717 /* Retrieve only DACL. */
3718 (void)GetNamedSecurityInfo(
3719 (LPSTR)fname,
3720 SE_FILE_OBJECT,
3721 DACL_SECURITY_INFORMATION,
3722 NULL,
3723 NULL,
3724 &p->pDacl,
3725 NULL,
3726 &p->pSecurityDescriptor);
3727 }
3728 if (p->pSecurityDescriptor == NULL)
3729 {
3730 mch_free_acl((vim_acl_T)p);
3731 p = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003732 }
3733 }
3734 }
3735
3736 return (vim_acl_T)p;
3737#endif
3738}
3739
Bram Moolenaar27515922013-06-29 15:36:26 +02003740#ifdef HAVE_ACL
3741/*
3742 * Check if "acl" contains inherited ACE.
3743 */
3744 static BOOL
3745is_acl_inherited(PACL acl)
3746{
3747 DWORD i;
3748 ACL_SIZE_INFORMATION acl_info;
3749 PACCESS_ALLOWED_ACE ace;
3750
3751 acl_info.AceCount = 0;
3752 GetAclInformation(acl, &acl_info, sizeof(acl_info), AclSizeInformation);
3753 for (i = 0; i < acl_info.AceCount; i++)
3754 {
3755 GetAce(acl, i, (LPVOID *)&ace);
3756 if (ace->Header.AceFlags & INHERITED_ACE)
3757 return TRUE;
3758 }
3759 return FALSE;
3760}
3761#endif
3762
Bram Moolenaar071d4272004-06-13 20:20:40 +00003763/*
3764 * Set the ACL of file "fname" to "acl" (unless it's NULL).
3765 * Errors are ignored.
3766 * This must only be called with "acl" equal to what mch_get_acl() returned.
3767 */
3768 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003769mch_set_acl(char_u *fname, vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003770{
3771#ifdef HAVE_ACL
3772 struct my_acl *p = (struct my_acl *)acl;
Bram Moolenaar27515922013-06-29 15:36:26 +02003773 SECURITY_INFORMATION sec_info = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003774
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003775 if (p != NULL)
Bram Moolenaar27515922013-06-29 15:36:26 +02003776 {
Bram Moolenaar27515922013-06-29 15:36:26 +02003777 WCHAR *wn = NULL;
Bram Moolenaar27515922013-06-29 15:36:26 +02003778
3779 /* Set security flags */
3780 if (p->pSidOwner)
3781 sec_info |= OWNER_SECURITY_INFORMATION;
3782 if (p->pSidGroup)
3783 sec_info |= GROUP_SECURITY_INFORMATION;
3784 if (p->pDacl)
3785 {
3786 sec_info |= DACL_SECURITY_INFORMATION;
3787 /* Do not inherit its parent's DACL.
3788 * If the DACL is inherited, Cygwin permissions would be changed.
3789 */
3790 if (!is_acl_inherited(p->pDacl))
3791 sec_info |= PROTECTED_DACL_SECURITY_INFORMATION;
3792 }
3793 if (p->pSacl)
3794 sec_info |= SACL_SECURITY_INFORMATION;
3795
Bram Moolenaar27515922013-06-29 15:36:26 +02003796 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3797 wn = enc_to_utf16(fname, NULL);
3798 if (wn != NULL)
3799 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003800 (void)SetNamedSecurityInfoW(
Bram Moolenaar27515922013-06-29 15:36:26 +02003801 wn, // Abstract filename
3802 SE_FILE_OBJECT, // File Object
3803 sec_info,
3804 p->pSidOwner, // Ownership information.
3805 p->pSidGroup, // Group membership.
3806 p->pDacl, // Discretionary information.
3807 p->pSacl // For auditing purposes.
3808 );
3809 vim_free(wn);
3810 }
3811 else
Bram Moolenaar27515922013-06-29 15:36:26 +02003812 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003813 (void)SetNamedSecurityInfo(
Bram Moolenaar27515922013-06-29 15:36:26 +02003814 (LPSTR)fname, // Abstract filename
3815 SE_FILE_OBJECT, // File Object
3816 sec_info,
3817 p->pSidOwner, // Ownership information.
3818 p->pSidGroup, // Group membership.
3819 p->pDacl, // Discretionary information.
3820 p->pSacl // For auditing purposes.
3821 );
3822 }
3823 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824#endif
3825}
3826
3827 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003828mch_free_acl(vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829{
3830#ifdef HAVE_ACL
3831 struct my_acl *p = (struct my_acl *)acl;
3832
3833 if (p != NULL)
3834 {
3835 LocalFree(p->pSecurityDescriptor); // Free the memory just in case
3836 vim_free(p);
3837 }
3838#endif
3839}
3840
3841#ifndef FEAT_GUI_W32
3842
3843/*
3844 * handler for ctrl-break, ctrl-c interrupts, and fatal events.
3845 */
3846 static BOOL WINAPI
3847handler_routine(
3848 DWORD dwCtrlType)
3849{
Bram Moolenaar589b1102017-08-12 16:39:05 +02003850 INPUT_RECORD ir;
3851 DWORD out;
3852
Bram Moolenaar071d4272004-06-13 20:20:40 +00003853 switch (dwCtrlType)
3854 {
3855 case CTRL_C_EVENT:
3856 if (ctrl_c_interrupts)
3857 g_fCtrlCPressed = TRUE;
3858 return TRUE;
3859
3860 case CTRL_BREAK_EVENT:
3861 g_fCBrkPressed = TRUE;
Bram Moolenaar589b1102017-08-12 16:39:05 +02003862 ctrl_break_was_pressed = TRUE;
3863 /* ReadConsoleInput is blocking, send a key event to continue. */
3864 ir.EventType = KEY_EVENT;
3865 ir.Event.KeyEvent.bKeyDown = TRUE;
3866 ir.Event.KeyEvent.wRepeatCount = 1;
3867 ir.Event.KeyEvent.wVirtualKeyCode = VK_CANCEL;
3868 ir.Event.KeyEvent.wVirtualScanCode = 0;
3869 ir.Event.KeyEvent.dwControlKeyState = 0;
3870 ir.Event.KeyEvent.uChar.UnicodeChar = 0;
3871 WriteConsoleInput(g_hConIn, &ir, 1, &out);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003872 return TRUE;
3873
3874 /* fatal events: shut down gracefully */
3875 case CTRL_CLOSE_EVENT:
3876 case CTRL_LOGOFF_EVENT:
3877 case CTRL_SHUTDOWN_EVENT:
3878 windgoto((int)Rows - 1, 0);
3879 g_fForceExit = TRUE;
3880
Bram Moolenaar0fde2902008-03-16 13:54:13 +00003881 vim_snprintf((char *)IObuff, IOSIZE, _("Vim: Caught %s event\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003882 (dwCtrlType == CTRL_CLOSE_EVENT
3883 ? _("close")
3884 : dwCtrlType == CTRL_LOGOFF_EVENT
3885 ? _("logoff")
3886 : _("shutdown")));
3887#ifdef DEBUG
3888 OutputDebugString(IObuff);
3889#endif
3890
3891 preserve_exit(); /* output IObuff, preserve files and exit */
3892
3893 return TRUE; /* not reached */
3894
3895 default:
3896 return FALSE;
3897 }
3898}
3899
3900
3901/*
3902 * set the tty in (raw) ? "raw" : "cooked" mode
3903 */
3904 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003905mch_settmode(int tmode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003906{
3907 DWORD cmodein;
3908 DWORD cmodeout;
3909 BOOL bEnableHandler;
3910
3911 GetConsoleMode(g_hConIn, &cmodein);
3912 GetConsoleMode(g_hConOut, &cmodeout);
3913 if (tmode == TMODE_RAW)
3914 {
3915 cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3916 ENABLE_ECHO_INPUT);
3917#ifdef FEAT_MOUSE
3918 if (g_fMouseActive)
3919 cmodein |= ENABLE_MOUSE_INPUT;
3920#endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01003921 cmodeout &= ~(
3922#ifdef FEAT_TERMGUICOLORS
3923 /* Do not turn off the ENABLE_PROCESSRD_OUTPUT flag when using
3924 * VTP. */
3925 ((vtp_working) ? 0 : ENABLE_PROCESSED_OUTPUT) |
3926#else
3927 ENABLE_PROCESSED_OUTPUT |
3928#endif
3929 ENABLE_WRAP_AT_EOL_OUTPUT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003930 bEnableHandler = TRUE;
3931 }
3932 else /* cooked */
3933 {
3934 cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3935 ENABLE_ECHO_INPUT);
3936 cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3937 bEnableHandler = FALSE;
3938 }
3939 SetConsoleMode(g_hConIn, cmodein);
3940 SetConsoleMode(g_hConOut, cmodeout);
3941 SetConsoleCtrlHandler(handler_routine, bEnableHandler);
3942
3943#ifdef MCH_WRITE_DUMP
3944 if (fdDump)
3945 {
3946 fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n",
3947 tmode == TMODE_RAW ? "raw" :
3948 tmode == TMODE_COOK ? "cooked" : "normal",
3949 cmodein, cmodeout);
3950 fflush(fdDump);
3951 }
3952#endif
3953}
3954
3955
3956/*
3957 * Get the size of the current window in `Rows' and `Columns'
3958 * Return OK when size could be determined, FAIL otherwise.
3959 */
3960 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003961mch_get_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003962{
3963 CONSOLE_SCREEN_BUFFER_INFO csbi;
3964
3965 if (!g_fTermcapMode && g_cbTermcap.IsValid)
3966 {
3967 /*
3968 * For some reason, we are trying to get the screen dimensions
3969 * even though we are not in termcap mode. The 'Rows' and 'Columns'
3970 * variables are really intended to mean the size of Vim screen
3971 * while in termcap mode.
3972 */
3973 Rows = g_cbTermcap.Info.dwSize.Y;
3974 Columns = g_cbTermcap.Info.dwSize.X;
3975 }
3976 else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3977 {
3978 Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3979 Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3980 }
3981 else
3982 {
3983 Rows = 25;
3984 Columns = 80;
3985 }
3986 return OK;
3987}
3988
3989/*
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02003990 * Resize console buffer to 'COORD'
3991 */
3992 static void
3993ResizeConBuf(
3994 HANDLE hConsole,
3995 COORD coordScreen)
3996{
3997 if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
3998 {
3999#ifdef MCH_WRITE_DUMP
4000 if (fdDump)
4001 {
4002 fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
4003 GetLastError());
4004 fflush(fdDump);
4005 }
4006#endif
4007 }
4008}
4009
4010/*
4011 * Resize console window size to 'srWindowRect'
4012 */
4013 static void
4014ResizeWindow(
4015 HANDLE hConsole,
4016 SMALL_RECT srWindowRect)
4017{
4018 if (!SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect))
4019 {
4020#ifdef MCH_WRITE_DUMP
4021 if (fdDump)
4022 {
4023 fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
4024 GetLastError());
4025 fflush(fdDump);
4026 }
4027#endif
4028 }
4029}
4030
4031/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004032 * Set a console window to `xSize' * `ySize'
4033 */
4034 static void
4035ResizeConBufAndWindow(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004036 HANDLE hConsole,
4037 int xSize,
4038 int ySize)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004039{
4040 CONSOLE_SCREEN_BUFFER_INFO csbi; /* hold current console buffer info */
4041 SMALL_RECT srWindowRect; /* hold the new console size */
4042 COORD coordScreen;
Bram Moolenaar2551c032018-08-23 22:38:31 +02004043 static int resized = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044
4045#ifdef MCH_WRITE_DUMP
4046 if (fdDump)
4047 {
4048 fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
4049 fflush(fdDump);
4050 }
4051#endif
4052
4053 /* get the largest size we can size the console window to */
4054 coordScreen = GetLargestConsoleWindowSize(hConsole);
4055
4056 /* define the new console window size and scroll position */
4057 srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
4058 srWindowRect.Right = (SHORT) (min(xSize, coordScreen.X) - 1);
4059 srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
4060
4061 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
4062 {
4063 int sx, sy;
4064
4065 sx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
4066 sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
4067 if (sy < ySize || sx < xSize)
4068 {
4069 /*
4070 * Increasing number of lines/columns, do buffer first.
4071 * Use the maximal size in x and y direction.
4072 */
4073 if (sy < ySize)
4074 coordScreen.Y = ySize;
4075 else
4076 coordScreen.Y = sy;
4077 if (sx < xSize)
4078 coordScreen.X = xSize;
4079 else
4080 coordScreen.X = sx;
4081 SetConsoleScreenBufferSize(hConsole, coordScreen);
4082 }
4083 }
4084
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02004085 // define the new console buffer size
Bram Moolenaar071d4272004-06-13 20:20:40 +00004086 coordScreen.X = xSize;
4087 coordScreen.Y = ySize;
4088
Bram Moolenaar2551c032018-08-23 22:38:31 +02004089 // In the new console call API, only the first time in reverse order
4090 if (!vtp_working || resized)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004091 {
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02004092 ResizeWindow(hConsole, srWindowRect);
4093 ResizeConBuf(hConsole, coordScreen);
4094 }
4095 else
4096 {
4097 ResizeConBuf(hConsole, coordScreen);
4098 ResizeWindow(hConsole, srWindowRect);
Bram Moolenaar2551c032018-08-23 22:38:31 +02004099 resized = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004100 }
4101}
4102
4103
4104/*
4105 * Set the console window to `Rows' * `Columns'
4106 */
4107 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004108mch_set_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004109{
4110 COORD coordScreen;
4111
4112 /* Don't change window size while still starting up */
4113 if (suppress_winsize != 0)
4114 {
4115 suppress_winsize = 2;
4116 return;
4117 }
4118
4119 if (term_console)
4120 {
4121 coordScreen = GetLargestConsoleWindowSize(g_hConOut);
4122
4123 /* Clamp Rows and Columns to reasonable values */
4124 if (Rows > coordScreen.Y)
4125 Rows = coordScreen.Y;
4126 if (Columns > coordScreen.X)
4127 Columns = coordScreen.X;
4128
4129 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
4130 }
4131}
4132
4133/*
4134 * Rows and/or Columns has changed.
4135 */
4136 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004137mch_new_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138{
4139 set_scroll_region(0, 0, Columns - 1, Rows - 1);
4140}
4141
4142
4143/*
4144 * Called when started up, to set the winsize that was delayed.
4145 */
4146 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004147mch_set_winsize_now(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148{
4149 if (suppress_winsize == 2)
4150 {
4151 suppress_winsize = 0;
4152 mch_set_shellsize();
4153 shell_resized();
4154 }
4155 suppress_winsize = 0;
4156}
4157#endif /* FEAT_GUI_W32 */
4158
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004159 static BOOL
4160vim_create_process(
Bram Moolenaar36c85b22013-12-12 20:25:44 +01004161 char *cmd,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004162 BOOL inherit_handles,
Bram Moolenaar3f1138e2014-01-05 13:29:26 +01004163 DWORD flags,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004164 STARTUPINFO *si,
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004165 PROCESS_INFORMATION *pi,
4166 LPVOID *env,
4167 char *cwd)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004168{
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004169 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4170 {
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004171 BOOL ret;
4172 WCHAR *wcmd, *wcwd = NULL;
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004173
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004174 wcmd = enc_to_utf16((char_u *)cmd, NULL);
4175 if (wcmd == NULL)
4176 goto fallback;
4177 if (cwd != NULL)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004178 {
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004179 wcwd = enc_to_utf16((char_u *)cwd, NULL);
4180 if (wcwd == NULL)
4181 {
4182 vim_free(wcmd);
4183 goto fallback;
4184 }
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004185 }
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004186
4187 ret = CreateProcessW(
4188 NULL, /* Executable name */
4189 wcmd, /* Command to execute */
4190 NULL, /* Process security attributes */
4191 NULL, /* Thread security attributes */
4192 inherit_handles, /* Inherit handles */
4193 flags, /* Creation flags */
4194 env, /* Environment */
4195 wcwd, /* Current directory */
4196 (LPSTARTUPINFOW)si, /* Startup information */
4197 pi); /* Process information */
4198 vim_free(wcmd);
4199 if (wcwd != NULL)
4200 vim_free(wcwd);
4201 return ret;
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004202 }
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004203fallback:
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004204 return CreateProcess(
4205 NULL, /* Executable name */
4206 cmd, /* Command to execute */
4207 NULL, /* Process security attributes */
4208 NULL, /* Thread security attributes */
4209 inherit_handles, /* Inherit handles */
4210 flags, /* Creation flags */
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004211 env, /* Environment */
4212 cwd, /* Current directory */
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004213 si, /* Startup information */
4214 pi); /* Process information */
4215}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216
4217
Bram Moolenaarb2964f22017-03-21 19:29:26 +01004218 static HINSTANCE
4219vim_shell_execute(
4220 char *cmd,
4221 INT n_show_cmd)
4222{
Bram Moolenaarb2964f22017-03-21 19:29:26 +01004223 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4224 {
4225 WCHAR *wcmd = enc_to_utf16((char_u *)cmd, NULL);
4226 if (wcmd != NULL)
4227 {
4228 HINSTANCE ret;
4229 ret = ShellExecuteW(NULL, NULL, wcmd, NULL, NULL, n_show_cmd);
4230 vim_free(wcmd);
4231 return ret;
4232 }
4233 }
Bram Moolenaarb2964f22017-03-21 19:29:26 +01004234 return ShellExecute(NULL, NULL, cmd, NULL, NULL, n_show_cmd);
4235}
4236
4237
Bram Moolenaar071d4272004-06-13 20:20:40 +00004238#if defined(FEAT_GUI_W32) || defined(PROTO)
4239
4240/*
4241 * Specialised version of system() for Win32 GUI mode.
4242 * This version proceeds as follows:
4243 * 1. Create a console window for use by the subprocess
4244 * 2. Run the subprocess (it gets the allocated console by default)
4245 * 3. Wait for the subprocess to terminate and get its exit code
4246 * 4. Prompt the user to press a key to close the console window
4247 */
4248 static int
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004249mch_system_classic(char *cmd, int options)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004250{
4251 STARTUPINFO si;
4252 PROCESS_INFORMATION pi;
4253 DWORD ret = 0;
4254 HWND hwnd = GetFocus();
4255
4256 si.cb = sizeof(si);
4257 si.lpReserved = NULL;
4258 si.lpDesktop = NULL;
4259 si.lpTitle = NULL;
4260 si.dwFlags = STARTF_USESHOWWINDOW;
4261 /*
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004262 * It's nicer to run a filter command in a minimized window.
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004263 * Don't activate the window to keep focus on Vim.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004264 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004265 if (options & SHELL_DOOUT)
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004266 si.wShowWindow = SW_SHOWMINNOACTIVE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004267 else
4268 si.wShowWindow = SW_SHOWNORMAL;
4269 si.cbReserved2 = 0;
4270 si.lpReserved2 = NULL;
4271
Bram Moolenaar071d4272004-06-13 20:20:40 +00004272 /* Now, run the command */
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004273 vim_create_process(cmd, FALSE,
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004274 CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE,
4275 &si, &pi, NULL, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004276
4277 /* Wait for the command to terminate before continuing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278 {
4279#ifdef FEAT_GUI
4280 int delay = 1;
4281
4282 /* Keep updating the window while waiting for the shell to finish. */
4283 for (;;)
4284 {
4285 MSG msg;
4286
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02004287 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004288 {
4289 TranslateMessage(&msg);
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02004290 pDispatchMessage(&msg);
Bram Moolenaare4195c52012-08-02 12:31:44 +02004291 delay = 1;
4292 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004293 }
4294 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4295 break;
4296
4297 /* We start waiting for a very short time and then increase it, so
4298 * that we respond quickly when the process is quick, and don't
4299 * consume too much overhead when it's slow. */
4300 if (delay < 50)
4301 delay += 10;
4302 }
4303#else
4304 WaitForSingleObject(pi.hProcess, INFINITE);
4305#endif
4306
4307 /* Get the command exit code */
4308 GetExitCodeProcess(pi.hProcess, &ret);
4309 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310
4311 /* Close the handles to the subprocess, so that it goes away */
4312 CloseHandle(pi.hThread);
4313 CloseHandle(pi.hProcess);
4314
4315 /* Try to get input focus back. Doesn't always work though. */
4316 PostMessage(hwnd, WM_SETFOCUS, 0, 0);
4317
4318 return ret;
4319}
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004320
4321/*
4322 * Thread launched by the gui to send the current buffer data to the
4323 * process. This way avoid to hang up vim totally if the children
4324 * process take a long time to process the lines.
4325 */
Bram Moolenaar4c38d662016-08-03 20:54:57 +02004326 static unsigned int __stdcall
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004327sub_process_writer(LPVOID param)
4328{
4329 HANDLE g_hChildStd_IN_Wr = param;
4330 linenr_T lnum = curbuf->b_op_start.lnum;
4331 DWORD len = 0;
4332 DWORD l;
4333 char_u *lp = ml_get(lnum);
4334 char_u *s;
4335 int written = 0;
4336
4337 for (;;)
4338 {
4339 l = (DWORD)STRLEN(lp + written);
4340 if (l == 0)
4341 len = 0;
4342 else if (lp[written] == NL)
4343 {
4344 /* NL -> NUL translation */
4345 WriteFile(g_hChildStd_IN_Wr, "", 1, &len, NULL);
4346 }
4347 else
4348 {
4349 s = vim_strchr(lp + written, NL);
4350 WriteFile(g_hChildStd_IN_Wr, (char *)lp + written,
4351 s == NULL ? l : (DWORD)(s - (lp + written)),
4352 &len, NULL);
4353 }
4354 if (len == (int)l)
4355 {
4356 /* Finished a line, add a NL, unless this line should not have
4357 * one. */
4358 if (lnum != curbuf->b_op_end.lnum
Bram Moolenaar34d72d42015-07-17 14:18:08 +02004359 || (!curbuf->b_p_bin
4360 && curbuf->b_p_fixeol)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004361 || (lnum != curbuf->b_no_eol_lnum
4362 && (lnum != curbuf->b_ml.ml_line_count
4363 || curbuf->b_p_eol)))
4364 {
Bram Moolenaar42335f52018-09-13 15:33:43 +02004365 WriteFile(g_hChildStd_IN_Wr, "\n", 1,
4366 (LPDWORD)&vim_ignored, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004367 }
4368
4369 ++lnum;
4370 if (lnum > curbuf->b_op_end.lnum)
4371 break;
4372
4373 lp = ml_get(lnum);
4374 written = 0;
4375 }
4376 else if (len > 0)
4377 written += len;
4378 }
4379
4380 /* finished all the lines, close pipe */
4381 CloseHandle(g_hChildStd_IN_Wr);
Bram Moolenaar86f2cd52016-08-02 21:55:17 +02004382 return 0;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004383}
4384
4385
4386# define BUFLEN 100 /* length for buffer, stolen from unix version */
4387
4388/*
4389 * This function read from the children's stdout and write the
4390 * data on screen or in the buffer accordingly.
4391 */
4392 static void
4393dump_pipe(int options,
4394 HANDLE g_hChildStd_OUT_Rd,
4395 garray_T *ga,
4396 char_u buffer[],
4397 DWORD *buffer_off)
4398{
4399 DWORD availableBytes = 0;
4400 DWORD i;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004401 int ret;
4402 DWORD len;
4403 DWORD toRead;
4404 int repeatCount;
4405
4406 /* we query the pipe to see if there is any data to read
4407 * to avoid to perform a blocking read */
4408 ret = PeekNamedPipe(g_hChildStd_OUT_Rd, /* pipe to query */
4409 NULL, /* optional buffer */
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02004410 0, /* buffer size */
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004411 NULL, /* number of read bytes */
4412 &availableBytes, /* available bytes total */
4413 NULL); /* byteLeft */
4414
4415 repeatCount = 0;
4416 /* We got real data in the pipe, read it */
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02004417 while (ret != 0 && availableBytes > 0)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004418 {
4419 repeatCount++;
Bram Moolenaara12a1612019-01-24 16:39:02 +01004420 toRead = (DWORD)(BUFLEN - *buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004421 toRead = availableBytes < toRead ? availableBytes : toRead;
Bram Moolenaara12a1612019-01-24 16:39:02 +01004422 ReadFile(g_hChildStd_OUT_Rd, buffer + *buffer_off, toRead , &len, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004423
4424 /* If we haven't read anything, there is a problem */
4425 if (len == 0)
4426 break;
4427
4428 availableBytes -= len;
4429
4430 if (options & SHELL_READ)
4431 {
4432 /* Do NUL -> NL translation, append NL separated
4433 * lines to the current buffer. */
4434 for (i = 0; i < len; ++i)
4435 {
4436 if (buffer[i] == NL)
4437 append_ga_line(ga);
4438 else if (buffer[i] == NUL)
4439 ga_append(ga, NL);
4440 else
4441 ga_append(ga, buffer[i]);
4442 }
4443 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004444 else if (has_mbyte)
4445 {
4446 int l;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004447 int c;
4448 char_u *p;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004449
4450 len += *buffer_off;
4451 buffer[len] = NUL;
4452
4453 /* Check if the last character in buffer[] is
4454 * incomplete, keep these bytes for the next
4455 * round. */
4456 for (p = buffer; p < buffer + len; p += l)
4457 {
Bram Moolenaard3c907b2016-08-17 21:32:09 +02004458 l = MB_CPTR2LEN(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004459 if (l == 0)
4460 l = 1; /* NUL byte? */
4461 else if (MB_BYTE2LEN(*p) != l)
4462 break;
4463 }
4464 if (p == buffer) /* no complete character */
4465 {
4466 /* avoid getting stuck at an illegal byte */
4467 if (len >= 12)
4468 ++p;
4469 else
4470 {
4471 *buffer_off = len;
4472 return;
4473 }
4474 }
4475 c = *p;
4476 *p = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004477 msg_puts((char *)buffer);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004478 if (p < buffer + len)
4479 {
4480 *p = c;
4481 *buffer_off = (DWORD)((buffer + len) - p);
4482 mch_memmove(buffer, p, *buffer_off);
4483 return;
4484 }
4485 *buffer_off = 0;
4486 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004487 else
4488 {
4489 buffer[len] = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004490 msg_puts((char *)buffer);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004491 }
4492
4493 windgoto(msg_row, msg_col);
4494 cursor_on();
4495 out_flush();
4496 }
4497}
4498
4499/*
4500 * Version of system to use for windows NT > 5.0 (Win2K), use pipe
4501 * for communication and doesn't open any new window.
4502 */
4503 static int
4504mch_system_piped(char *cmd, int options)
4505{
4506 STARTUPINFO si;
4507 PROCESS_INFORMATION pi;
4508 DWORD ret = 0;
4509
4510 HANDLE g_hChildStd_IN_Rd = NULL;
4511 HANDLE g_hChildStd_IN_Wr = NULL;
4512 HANDLE g_hChildStd_OUT_Rd = NULL;
4513 HANDLE g_hChildStd_OUT_Wr = NULL;
4514
4515 char_u buffer[BUFLEN + 1]; /* reading buffer + size */
4516 DWORD len;
4517
4518 /* buffer used to receive keys */
4519 char_u ta_buf[BUFLEN + 1]; /* TypeAHead */
4520 int ta_len = 0; /* valid bytes in ta_buf[] */
4521
4522 DWORD i;
4523 int c;
4524 int noread_cnt = 0;
4525 garray_T ga;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004526 int delay = 1;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004527 DWORD buffer_off = 0; /* valid bytes in buffer[] */
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004528 char *p = NULL;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004529
4530 SECURITY_ATTRIBUTES saAttr;
4531
4532 /* Set the bInheritHandle flag so pipe handles are inherited. */
4533 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
4534 saAttr.bInheritHandle = TRUE;
4535 saAttr.lpSecurityDescriptor = NULL;
4536
4537 if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)
4538 /* Ensure the read handle to the pipe for STDOUT is not inherited. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004539 || ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004540 /* Create a pipe for the child process's STDIN. */
4541 || ! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)
4542 /* Ensure the write handle to the pipe for STDIN is not inherited. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004543 || ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004544 {
4545 CloseHandle(g_hChildStd_IN_Rd);
4546 CloseHandle(g_hChildStd_IN_Wr);
4547 CloseHandle(g_hChildStd_OUT_Rd);
4548 CloseHandle(g_hChildStd_OUT_Wr);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004549 msg_puts(_("\nCannot create pipes\n"));
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004550 }
4551
4552 si.cb = sizeof(si);
4553 si.lpReserved = NULL;
4554 si.lpDesktop = NULL;
4555 si.lpTitle = NULL;
4556 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
4557
4558 /* set-up our file redirection */
4559 si.hStdError = g_hChildStd_OUT_Wr;
4560 si.hStdOutput = g_hChildStd_OUT_Wr;
4561 si.hStdInput = g_hChildStd_IN_Rd;
4562 si.wShowWindow = SW_HIDE;
4563 si.cbReserved2 = 0;
4564 si.lpReserved2 = NULL;
4565
4566 if (options & SHELL_READ)
4567 ga_init2(&ga, 1, BUFLEN);
4568
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004569 if (cmd != NULL)
4570 {
4571 p = (char *)vim_strsave((char_u *)cmd);
4572 if (p != NULL)
4573 unescape_shellxquote((char_u *)p, p_sxe);
4574 else
4575 p = cmd;
4576 }
4577
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004578 /* Now, run the command.
4579 * About "Inherit handles" being TRUE: this command can be litigious,
4580 * handle inheritance was deactivated for pending temp file, but, if we
4581 * deactivate it, the pipes don't work for some reason. */
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004582 vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE,
4583 &si, &pi, NULL, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004584
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004585 if (p != cmd)
4586 vim_free(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004587
4588 /* Close our unused side of the pipes */
4589 CloseHandle(g_hChildStd_IN_Rd);
4590 CloseHandle(g_hChildStd_OUT_Wr);
4591
4592 if (options & SHELL_WRITE)
4593 {
Bram Moolenaar86f2cd52016-08-02 21:55:17 +02004594 HANDLE thread = (HANDLE)
4595 _beginthreadex(NULL, /* security attributes */
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004596 0, /* default stack size */
4597 sub_process_writer, /* function to be executed */
4598 g_hChildStd_IN_Wr, /* parameter */
4599 0, /* creation flag, start immediately */
4600 NULL); /* we don't care about thread id */
4601 CloseHandle(thread);
4602 g_hChildStd_IN_Wr = NULL;
4603 }
4604
4605 /* Keep updating the window while waiting for the shell to finish. */
4606 for (;;)
4607 {
4608 MSG msg;
4609
Bram Moolenaar175d0702013-12-11 18:36:33 +01004610 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004611 {
4612 TranslateMessage(&msg);
Bram Moolenaar175d0702013-12-11 18:36:33 +01004613 pDispatchMessage(&msg);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004614 }
4615
4616 /* write pipe information in the window */
4617 if ((options & (SHELL_READ|SHELL_WRITE))
4618# ifdef FEAT_GUI
4619 || gui.in_use
4620# endif
4621 )
4622 {
4623 len = 0;
4624 if (!(options & SHELL_EXPAND)
4625 && ((options &
4626 (SHELL_READ|SHELL_WRITE|SHELL_COOKED))
4627 != (SHELL_READ|SHELL_WRITE|SHELL_COOKED)
4628# ifdef FEAT_GUI
4629 || gui.in_use
4630# endif
4631 )
4632 && (ta_len > 0 || noread_cnt > 4))
4633 {
4634 if (ta_len == 0)
4635 {
4636 /* Get extra characters when we don't have any. Reset the
4637 * counter and timer. */
4638 noread_cnt = 0;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004639 len = ui_inchar(ta_buf, BUFLEN, 10L, 0);
4640 }
4641 if (ta_len > 0 || len > 0)
4642 {
4643 /*
4644 * For pipes: Check for CTRL-C: send interrupt signal to
4645 * child. Check for CTRL-D: EOF, close pipe to child.
4646 */
4647 if (len == 1 && cmd != NULL)
4648 {
4649 if (ta_buf[ta_len] == Ctrl_C)
4650 {
4651 /* Learn what exit code is expected, for
4652 * now put 9 as SIGKILL */
4653 TerminateProcess(pi.hProcess, 9);
4654 }
4655 if (ta_buf[ta_len] == Ctrl_D)
4656 {
4657 CloseHandle(g_hChildStd_IN_Wr);
4658 g_hChildStd_IN_Wr = NULL;
4659 }
4660 }
4661
4662 /* replace K_BS by <BS> and K_DEL by <DEL> */
4663 for (i = ta_len; i < ta_len + len; ++i)
4664 {
4665 if (ta_buf[i] == CSI && len - i > 2)
4666 {
4667 c = TERMCAP2KEY(ta_buf[i + 1], ta_buf[i + 2]);
4668 if (c == K_DEL || c == K_KDEL || c == K_BS)
4669 {
4670 mch_memmove(ta_buf + i + 1, ta_buf + i + 3,
4671 (size_t)(len - i - 2));
4672 if (c == K_DEL || c == K_KDEL)
4673 ta_buf[i] = DEL;
4674 else
4675 ta_buf[i] = Ctrl_H;
4676 len -= 2;
4677 }
4678 }
4679 else if (ta_buf[i] == '\r')
4680 ta_buf[i] = '\n';
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004681 if (has_mbyte)
4682 i += (*mb_ptr2len_len)(ta_buf + i,
4683 ta_len + len - i) - 1;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004684 }
4685
4686 /*
4687 * For pipes: echo the typed characters. For a pty this
4688 * does not seem to work.
4689 */
4690 for (i = ta_len; i < ta_len + len; ++i)
4691 {
4692 if (ta_buf[i] == '\n' || ta_buf[i] == '\b')
4693 msg_putchar(ta_buf[i]);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004694 else if (has_mbyte)
4695 {
4696 int l = (*mb_ptr2len)(ta_buf + i);
4697
4698 msg_outtrans_len(ta_buf + i, l);
4699 i += l - 1;
4700 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004701 else
4702 msg_outtrans_len(ta_buf + i, 1);
4703 }
4704 windgoto(msg_row, msg_col);
4705 out_flush();
4706
4707 ta_len += len;
4708
4709 /*
4710 * Write the characters to the child, unless EOF has been
4711 * typed for pipes. Write one character at a time, to
4712 * avoid losing too much typeahead. When writing buffer
4713 * lines, drop the typed characters (only check for
4714 * CTRL-C).
4715 */
4716 if (options & SHELL_WRITE)
4717 ta_len = 0;
4718 else if (g_hChildStd_IN_Wr != NULL)
4719 {
4720 WriteFile(g_hChildStd_IN_Wr, (char*)ta_buf,
4721 1, &len, NULL);
4722 // if we are typing in, we want to keep things reactive
4723 delay = 1;
4724 if (len > 0)
4725 {
4726 ta_len -= len;
4727 mch_memmove(ta_buf, ta_buf + len, ta_len);
4728 }
4729 }
4730 }
4731 }
4732 }
4733
4734 if (ta_len)
4735 ui_inchar_undo(ta_buf, ta_len);
4736
4737 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4738 {
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004739 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004740 break;
4741 }
4742
4743 ++noread_cnt;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004744 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004745
4746 /* We start waiting for a very short time and then increase it, so
4747 * that we respond quickly when the process is quick, and don't
4748 * consume too much overhead when it's slow. */
4749 if (delay < 50)
4750 delay += 10;
4751 }
4752
4753 /* Close the pipe */
4754 CloseHandle(g_hChildStd_OUT_Rd);
4755 if (g_hChildStd_IN_Wr != NULL)
4756 CloseHandle(g_hChildStd_IN_Wr);
4757
4758 WaitForSingleObject(pi.hProcess, INFINITE);
4759
4760 /* Get the command exit code */
4761 GetExitCodeProcess(pi.hProcess, &ret);
4762
4763 if (options & SHELL_READ)
4764 {
4765 if (ga.ga_len > 0)
4766 {
4767 append_ga_line(&ga);
4768 /* remember that the NL was missing */
4769 curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
4770 }
4771 else
4772 curbuf->b_no_eol_lnum = 0;
4773 ga_clear(&ga);
4774 }
4775
4776 /* Close the handles to the subprocess, so that it goes away */
4777 CloseHandle(pi.hThread);
4778 CloseHandle(pi.hProcess);
4779
4780 return ret;
4781}
4782
4783 static int
4784mch_system(char *cmd, int options)
4785{
4786 /* if we can pipe and the shelltemp option is off */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004787 if (!p_stmp)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004788 return mch_system_piped(cmd, options);
4789 else
4790 return mch_system_classic(cmd, options);
4791}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004792#else
4793
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004794 static int
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01004795mch_system(char *cmd, int options)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004796{
4797 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4798 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004799 WCHAR *wcmd = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004800 if (wcmd != NULL)
4801 {
4802 int ret = _wsystem(wcmd);
4803 vim_free(wcmd);
4804 return ret;
4805 }
4806 }
4807 return system(cmd);
4808}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004809
4810#endif
4811
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004812#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
4813/*
4814 * Use a terminal window to run a shell command in.
4815 */
4816 static int
4817mch_call_shell_terminal(
4818 char_u *cmd,
4819 int options UNUSED) /* SHELL_*, see vim.h */
4820{
4821 jobopt_T opt;
4822 char_u *newcmd = NULL;
4823 typval_T argvar[2];
4824 long_u cmdlen;
4825 int retval = -1;
4826 buf_T *buf;
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004827 job_T *job;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004828 aco_save_T aco;
4829 oparg_T oa; /* operator arguments */
4830
Bram Moolenaar42f652f2018-03-19 21:44:37 +01004831 if (cmd == NULL)
4832 cmdlen = STRLEN(p_sh) + 1;
4833 else
4834 cmdlen = STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004835 newcmd = lalloc(cmdlen, TRUE);
4836 if (newcmd == NULL)
4837 return 255;
Bram Moolenaar42f652f2018-03-19 21:44:37 +01004838 if (cmd == NULL)
4839 {
4840 STRCPY(newcmd, p_sh);
4841 ch_log(NULL, "starting terminal to run a shell");
4842 }
4843 else
4844 {
4845 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s", p_sh, p_shcf, cmd);
4846 ch_log(NULL, "starting terminal for system command '%s'", cmd);
4847 }
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004848
4849 init_job_options(&opt);
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004850
4851 argvar[0].v_type = VAR_STRING;
4852 argvar[0].vval.v_string = newcmd;
4853 argvar[1].v_type = VAR_UNKNOWN;
4854 buf = term_start(argvar, NULL, &opt, TERM_START_SYSTEM);
Bram Moolenaar81c3c89a2018-03-20 11:41:44 +01004855 if (buf == NULL)
4856 return 255;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004857
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004858 job = term_getjob(buf->b_term);
4859 ++job->jv_refcount;
4860
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004861 /* Find a window to make "buf" curbuf. */
4862 aucmd_prepbuf(&aco, buf);
4863
4864 clear_oparg(&oa);
4865 while (term_use_loop())
4866 {
4867 if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
4868 {
4869 /* If terminal_loop() returns OK we got a key that is handled
4870 * in Normal model. We don't do redrawing anyway. */
4871 if (terminal_loop(TRUE) == OK)
4872 normal_cmd(&oa, TRUE);
4873 }
4874 else
4875 normal_cmd(&oa, TRUE);
4876 }
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004877 retval = job->jv_exitval;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004878 ch_log(NULL, "system command finished");
4879
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004880 job_unref(job);
4881
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004882 /* restore curwin/curbuf and a few other things */
4883 aucmd_restbuf(&aco);
4884
4885 wait_return(TRUE);
4886 do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
4887
4888 vim_free(newcmd);
4889 return retval;
4890}
4891#endif
4892
Bram Moolenaar071d4272004-06-13 20:20:40 +00004893/*
4894 * Either execute a command by calling the shell or start a new shell
4895 */
4896 int
4897mch_call_shell(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004898 char_u *cmd,
4899 int options) /* SHELL_*, see vim.h */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004900{
4901 int x = 0;
4902 int tmode = cur_tmode;
4903#ifdef FEAT_TITLE
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004904 char szShellTitle[512];
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004905 int did_set_title = FALSE;
4906
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004907 /* Change the title to reflect that we are in a subshell. */
4908 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4909 {
4910 WCHAR szShellTitle[512];
4911
4912 if (GetConsoleTitleW(szShellTitle,
4913 sizeof(szShellTitle)/sizeof(WCHAR) - 4) > 0)
4914 {
4915 if (cmd == NULL)
4916 wcscat(szShellTitle, L" :sh");
4917 else
4918 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004919 WCHAR *wn = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004920
4921 if (wn != NULL)
4922 {
4923 wcscat(szShellTitle, L" - !");
4924 if ((wcslen(szShellTitle) + wcslen(wn) <
4925 sizeof(szShellTitle)/sizeof(WCHAR)))
4926 wcscat(szShellTitle, wn);
4927 SetConsoleTitleW(szShellTitle);
4928 vim_free(wn);
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004929 did_set_title = TRUE;
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004930 }
4931 }
4932 }
4933 }
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004934 if (!did_set_title)
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004935 /* Change the title to reflect that we are in a subshell. */
4936 if (GetConsoleTitle(szShellTitle, sizeof(szShellTitle) - 4) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004937 {
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004938 if (cmd == NULL)
4939 strcat(szShellTitle, " :sh");
4940 else
4941 {
4942 strcat(szShellTitle, " - !");
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004943 if ((strlen(szShellTitle) + strlen((char *)cmd)
4944 < sizeof(szShellTitle)))
4945 strcat(szShellTitle, (char *)cmd);
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004946 }
4947 SetConsoleTitle(szShellTitle);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004948 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004949#endif
4950
4951 out_flush();
4952
4953#ifdef MCH_WRITE_DUMP
4954 if (fdDump)
4955 {
4956 fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
4957 fflush(fdDump);
4958 }
4959#endif
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004960#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
4961 /* TODO: make the terminal window work with input or output redirected. */
4962 if (vim_strchr(p_go, GO_TERMINAL) != NULL
4963 && (options & (SHELL_FILTER|SHELL_DOOUT|SHELL_WRITE|SHELL_READ)) == 0)
4964 {
4965 /* Use a terminal window to run the command in. */
4966 x = mch_call_shell_terminal(cmd, options);
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01004967# ifdef FEAT_TITLE
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004968 resettitle();
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01004969# endif
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004970 return x;
4971 }
4972#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004973
4974 /*
4975 * Catch all deadly signals while running the external command, because a
4976 * CTRL-C, Ctrl-Break or illegal instruction might otherwise kill us.
4977 */
4978 signal(SIGINT, SIG_IGN);
4979#if defined(__GNUC__) && !defined(__MINGW32__)
4980 signal(SIGKILL, SIG_IGN);
4981#else
4982 signal(SIGBREAK, SIG_IGN);
4983#endif
4984 signal(SIGILL, SIG_IGN);
4985 signal(SIGFPE, SIG_IGN);
4986 signal(SIGSEGV, SIG_IGN);
4987 signal(SIGTERM, SIG_IGN);
4988 signal(SIGABRT, SIG_IGN);
4989
4990 if (options & SHELL_COOKED)
4991 settmode(TMODE_COOK); /* set to normal mode */
4992
4993 if (cmd == NULL)
4994 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004995 x = mch_system((char *)p_sh, options);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004996 }
4997 else
4998 {
4999 /* we use "command" or "cmd" to start the shell; slow but easy */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005000 char_u *newcmd = NULL;
5001 char_u *cmdbase = cmd;
5002 long_u cmdlen;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005003
5004 /* Skip a leading ", ( and "(. */
5005 if (*cmdbase == '"' )
5006 ++cmdbase;
5007 if (*cmdbase == '(')
5008 ++cmdbase;
5009
Bram Moolenaar1c465442017-03-12 20:10:05 +01005010 if ((STRNICMP(cmdbase, "start", 5) == 0) && VIM_ISWHITE(cmdbase[5]))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005011 {
5012 STARTUPINFO si;
5013 PROCESS_INFORMATION pi;
5014 DWORD flags = CREATE_NEW_CONSOLE;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01005015 INT n_show_cmd = SW_SHOWNORMAL;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005016 char_u *p;
5017
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01005018 ZeroMemory(&si, sizeof(si));
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005019 si.cb = sizeof(si);
5020 si.lpReserved = NULL;
5021 si.lpDesktop = NULL;
5022 si.lpTitle = NULL;
5023 si.dwFlags = 0;
5024 si.cbReserved2 = 0;
5025 si.lpReserved2 = NULL;
5026
5027 cmdbase = skipwhite(cmdbase + 5);
5028 if ((STRNICMP(cmdbase, "/min", 4) == 0)
Bram Moolenaar1c465442017-03-12 20:10:05 +01005029 && VIM_ISWHITE(cmdbase[4]))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005030 {
5031 cmdbase = skipwhite(cmdbase + 4);
5032 si.dwFlags = STARTF_USESHOWWINDOW;
5033 si.wShowWindow = SW_SHOWMINNOACTIVE;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01005034 n_show_cmd = SW_SHOWMINNOACTIVE;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005035 }
5036 else if ((STRNICMP(cmdbase, "/b", 2) == 0)
Bram Moolenaar1c465442017-03-12 20:10:05 +01005037 && VIM_ISWHITE(cmdbase[2]))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005038 {
5039 cmdbase = skipwhite(cmdbase + 2);
5040 flags = CREATE_NO_WINDOW;
5041 si.dwFlags = STARTF_USESTDHANDLES;
5042 si.hStdInput = CreateFile("\\\\.\\NUL", // File name
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005043 GENERIC_READ, // Access flags
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005044 0, // Share flags
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005045 NULL, // Security att.
5046 OPEN_EXISTING, // Open flags
5047 FILE_ATTRIBUTE_NORMAL, // File att.
5048 NULL); // Temp file
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005049 si.hStdOutput = si.hStdInput;
5050 si.hStdError = si.hStdInput;
5051 }
5052
5053 /* Remove a trailing ", ) and )" if they have a match
5054 * at the start of the command. */
5055 if (cmdbase > cmd)
5056 {
5057 p = cmdbase + STRLEN(cmdbase);
5058 if (p > cmdbase && p[-1] == '"' && *cmd == '"')
5059 *--p = NUL;
5060 if (p > cmdbase && p[-1] == ')'
5061 && (*cmd =='(' || cmd[1] == '('))
5062 *--p = NUL;
5063 }
5064
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005065 newcmd = cmdbase;
5066 unescape_shellxquote(cmdbase, p_sxe);
5067
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005068 /*
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005069 * If creating new console, arguments are passed to the
5070 * 'cmd.exe' as-is. If it's not, arguments are not treated
5071 * correctly for current 'cmd.exe'. So unescape characters in
5072 * shellxescape except '|' for avoiding to be treated as
5073 * argument to them. Pass the arguments to sub-shell.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005074 */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005075 if (flags != CREATE_NEW_CONSOLE)
5076 {
5077 char_u *subcmd;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01005078 char_u *cmd_shell = mch_getenv("COMSPEC");
5079
5080 if (cmd_shell == NULL || *cmd_shell == NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005081 cmd_shell = (char_u *)default_shell();
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005082
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005083 subcmd = vim_strsave_escaped_ext(cmdbase,
5084 (char_u *)"|", '^', FALSE);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005085 if (subcmd != NULL)
5086 {
5087 /* make "cmd.exe /c arguments" */
5088 cmdlen = STRLEN(cmd_shell) + STRLEN(subcmd) + 5;
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005089 newcmd = lalloc(cmdlen, TRUE);
5090 if (newcmd != NULL)
5091 vim_snprintf((char *)newcmd, cmdlen, "%s /c %s",
Bram Moolenaaree7d1002012-02-22 15:34:08 +01005092 cmd_shell, subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005093 else
5094 newcmd = cmdbase;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01005095 vim_free(subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005096 }
5097 }
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005098
5099 /*
5100 * Now, start the command as a process, so that it doesn't
5101 * inherit our handles which causes unpleasant dangling swap
5102 * files if we exit before the spawned process
5103 */
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005104 if (vim_create_process((char *)newcmd, FALSE, flags,
5105 &si, &pi, NULL, NULL))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005106 x = 0;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01005107 else if (vim_shell_execute((char *)newcmd, n_show_cmd)
5108 > (HINSTANCE)32)
5109 x = 0;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005110 else
5111 {
5112 x = -1;
5113#ifdef FEAT_GUI_W32
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005114 emsg(_("E371: Command not found"));
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005115#endif
5116 }
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005117
5118 if (newcmd != cmdbase)
5119 vim_free(newcmd);
5120
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01005121 if (si.dwFlags == STARTF_USESTDHANDLES && si.hStdInput != NULL)
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005122 {
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01005123 /* Close the handle to \\.\NUL created above. */
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005124 CloseHandle(si.hStdInput);
5125 }
5126 /* Close the handles to the subprocess, so that it goes away */
5127 CloseHandle(pi.hThread);
5128 CloseHandle(pi.hProcess);
5129 }
5130 else
5131 {
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005132 cmdlen = (
Bram Moolenaar071d4272004-06-13 20:20:40 +00005133#ifdef FEAT_GUI_W32
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005134 (!p_stmp ? 0 : STRLEN(vimrun_path)) +
Bram Moolenaar071d4272004-06-13 20:20:40 +00005135#endif
Bram Moolenaar0fde2902008-03-16 13:54:13 +00005136 STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10);
5137
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005138 newcmd = lalloc(cmdlen, TRUE);
5139 if (newcmd != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005140 {
5141#if defined(FEAT_GUI_W32)
5142 if (need_vimrun_warning)
5143 {
Bram Moolenaar63e43442016-11-19 17:28:44 +01005144 char *msg = _("VIMRUN.EXE not found in your $PATH.\n"
5145 "External commands will not pause after completion.\n"
5146 "See :help win32-vimrun for more information.");
5147 char *title = _("Vim Warning");
Bram Moolenaar63e43442016-11-19 17:28:44 +01005148 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
5149 {
5150 WCHAR *wmsg = enc_to_utf16((char_u *)msg, NULL);
5151 WCHAR *wtitle = enc_to_utf16((char_u *)title, NULL);
5152
5153 if (wmsg != NULL && wtitle != NULL)
5154 MessageBoxW(NULL, wmsg, wtitle, MB_ICONWARNING);
5155 vim_free(wmsg);
5156 vim_free(wtitle);
5157 }
5158 else
Bram Moolenaar63e43442016-11-19 17:28:44 +01005159 MessageBox(NULL, msg, title, MB_ICONWARNING);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005160 need_vimrun_warning = FALSE;
5161 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005162 if (!s_dont_use_vimrun && p_stmp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005163 /* Use vimrun to execute the command. It opens a console
5164 * window, which can be closed without killing Vim. */
Bram Moolenaarcc448b32010-07-14 16:52:17 +02005165 vim_snprintf((char *)newcmd, cmdlen, "%s%s%s %s %s",
Bram Moolenaar071d4272004-06-13 20:20:40 +00005166 vimrun_path,
5167 (msg_silent != 0 || (options & SHELL_DOOUT))
5168 ? "-s " : "",
5169 p_sh, p_shcf, cmd);
5170 else
5171#endif
Bram Moolenaarcc448b32010-07-14 16:52:17 +02005172 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s",
Bram Moolenaar0fde2902008-03-16 13:54:13 +00005173 p_sh, p_shcf, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005174 x = mch_system((char *)newcmd, options);
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005175 vim_free(newcmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005176 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005177 }
5178 }
5179
5180 if (tmode == TMODE_RAW)
5181 settmode(TMODE_RAW); /* set to raw mode */
5182
5183 /* Print the return value, unless "vimrun" was used. */
5184 if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent
5185#if defined(FEAT_GUI_W32)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005186 && ((options & SHELL_DOOUT) || s_dont_use_vimrun || !p_stmp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005187#endif
5188 )
5189 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005190 smsg(_("shell returned %d"), x);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005191 msg_putchar('\n');
5192 }
5193#ifdef FEAT_TITLE
5194 resettitle();
5195#endif
5196
5197 signal(SIGINT, SIG_DFL);
5198#if defined(__GNUC__) && !defined(__MINGW32__)
5199 signal(SIGKILL, SIG_DFL);
5200#else
5201 signal(SIGBREAK, SIG_DFL);
5202#endif
5203 signal(SIGILL, SIG_DFL);
5204 signal(SIGFPE, SIG_DFL);
5205 signal(SIGSEGV, SIG_DFL);
5206 signal(SIGTERM, SIG_DFL);
5207 signal(SIGABRT, SIG_DFL);
5208
5209 return x;
5210}
5211
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01005212#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005213 static HANDLE
5214job_io_file_open(
Bram Moolenaar972c3b82017-01-12 21:44:49 +01005215 char_u *fname,
5216 DWORD dwDesiredAccess,
5217 DWORD dwShareMode,
5218 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
5219 DWORD dwCreationDisposition,
5220 DWORD dwFlagsAndAttributes)
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005221{
5222 HANDLE h;
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005223 WCHAR *wn = NULL;
Bram Moolenaara12a1612019-01-24 16:39:02 +01005224
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005225 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
5226 {
Bram Moolenaar972c3b82017-01-12 21:44:49 +01005227 wn = enc_to_utf16(fname, NULL);
5228 if (wn != NULL)
5229 {
5230 h = CreateFileW(wn, dwDesiredAccess, dwShareMode,
5231 lpSecurityAttributes, dwCreationDisposition,
5232 dwFlagsAndAttributes, NULL);
5233 vim_free(wn);
5234 }
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005235 }
5236 if (wn == NULL)
Bram Moolenaar972c3b82017-01-12 21:44:49 +01005237 h = CreateFile((LPCSTR)fname, dwDesiredAccess, dwShareMode,
5238 lpSecurityAttributes, dwCreationDisposition,
5239 dwFlagsAndAttributes, NULL);
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005240 return h;
5241}
5242
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005243/*
5244 * Turn the dictionary "env" into a NUL separated list that can be used as the
5245 * environment argument of vim_create_process().
5246 */
Bram Moolenaarba6febd2017-10-30 21:56:23 +01005247 void
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005248win32_build_env(dict_T *env, garray_T *gap, int is_terminal)
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005249{
5250 hashitem_T *hi;
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005251 long_u todo = env != NULL ? env->dv_hashtab.ht_used : 0;
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005252 LPVOID base = GetEnvironmentStringsW();
5253
5254 /* for last \0 */
5255 if (ga_grow(gap, 1) == FAIL)
5256 return;
5257
5258 if (base)
5259 {
5260 WCHAR *p = (WCHAR*) base;
5261
5262 /* for last \0 */
5263 if (ga_grow(gap, 1) == FAIL)
5264 return;
5265
5266 while (*p != 0 || *(p + 1) != 0)
5267 {
5268 if (ga_grow(gap, 1) == OK)
5269 *((WCHAR*)gap->ga_data + gap->ga_len++) = *p;
5270 p++;
5271 }
5272 FreeEnvironmentStrings(base);
5273 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
5274 }
5275
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005276 if (env != NULL)
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005277 {
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005278 for (hi = env->dv_hashtab.ht_array; todo > 0; ++hi)
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005279 {
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005280 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005281 {
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005282 typval_T *item = &dict_lookup(hi)->di_tv;
5283 WCHAR *wkey = enc_to_utf16((char_u *)hi->hi_key, NULL);
Bram Moolenaard155d7a2018-12-21 16:04:21 +01005284 WCHAR *wval = enc_to_utf16(tv_get_string(item), NULL);
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005285 --todo;
5286 if (wkey != NULL && wval != NULL)
5287 {
5288 size_t n;
5289 size_t lkey = wcslen(wkey);
5290 size_t lval = wcslen(wval);
Bram Moolenaar60104f12017-08-14 23:25:04 +02005291
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005292 if (ga_grow(gap, (int)(lkey + lval + 2)) != OK)
5293 continue;
5294 for (n = 0; n < lkey; n++)
5295 *((WCHAR*)gap->ga_data + gap->ga_len++) = wkey[n];
5296 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'=';
5297 for (n = 0; n < lval; n++)
5298 *((WCHAR*)gap->ga_data + gap->ga_len++) = wval[n];
5299 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
5300 }
5301 if (wkey != NULL) vim_free(wkey);
5302 if (wval != NULL) vim_free(wval);
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005303 }
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005304 }
5305 }
5306
Bram Moolenaar493359e2018-06-12 20:25:52 +02005307# if defined(FEAT_CLIENTSERVER) || defined(FEAT_TERMINAL)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005308 {
Bram Moolenaar493359e2018-06-12 20:25:52 +02005309# ifdef FEAT_CLIENTSERVER
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005310 char_u *servername = get_vim_var_str(VV_SEND_SERVER);
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005311 size_t servername_len = STRLEN(servername);
Bram Moolenaar493359e2018-06-12 20:25:52 +02005312# endif
5313# ifdef FEAT_TERMINAL
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005314 char_u *version = get_vim_var_str(VV_VERSION);
5315 size_t version_len = STRLEN(version);
Bram Moolenaar493359e2018-06-12 20:25:52 +02005316# endif
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005317 // size of "VIM_SERVERNAME=" and value,
5318 // plus "VIM_TERMINAL=" and value,
5319 // plus two terminating NULs
5320 size_t n = 0
Bram Moolenaar493359e2018-06-12 20:25:52 +02005321# ifdef FEAT_CLIENTSERVER
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005322 + 15 + servername_len
Bram Moolenaar493359e2018-06-12 20:25:52 +02005323# endif
5324# ifdef FEAT_TERMINAL
5325 + 13 + version_len + 2
5326# endif
5327 ;
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005328
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005329 if (ga_grow(gap, (int)n) == OK)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005330 {
Bram Moolenaar493359e2018-06-12 20:25:52 +02005331# ifdef FEAT_CLIENTSERVER
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005332 for (n = 0; n < 15; n++)
5333 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5334 (WCHAR)"VIM_SERVERNAME="[n];
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005335 for (n = 0; n < servername_len; n++)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005336 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5337 (WCHAR)servername[n];
5338 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
Bram Moolenaar493359e2018-06-12 20:25:52 +02005339# endif
5340# ifdef FEAT_TERMINAL
5341 if (is_terminal)
5342 {
5343 for (n = 0; n < 13; n++)
5344 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5345 (WCHAR)"VIM_TERMINAL="[n];
5346 for (n = 0; n < version_len; n++)
5347 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5348 (WCHAR)version[n];
5349 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
5350 }
5351# endif
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005352 }
5353 }
Bram Moolenaar79c6b512018-06-12 21:11:12 +02005354# endif
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005355}
5356
Bram Moolenaarb091f302019-01-19 14:37:00 +01005357/*
5358 * Create a pair of pipes.
5359 * Return TRUE for success, FALSE for failure.
5360 */
5361 static BOOL
5362create_pipe_pair(HANDLE handles[2])
5363{
5364 static LONG s;
5365 char name[64];
5366 SECURITY_ATTRIBUTES sa;
5367
5368 sprintf(name, "\\\\?\\pipe\\vim-%08lx-%08lx",
5369 GetCurrentProcessId(),
5370 InterlockedIncrement(&s));
5371
5372 // Create named pipe. Max size of named pipe is 65535.
5373 handles[1] = CreateNamedPipe(
5374 name,
5375 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
5376 PIPE_TYPE_BYTE | PIPE_NOWAIT,
Bram Moolenaar24058382019-01-24 23:11:49 +01005377 1, MAX_NAMED_PIPE_SIZE, 0, 0, NULL);
Bram Moolenaarb091f302019-01-19 14:37:00 +01005378
5379 if (handles[1] == INVALID_HANDLE_VALUE)
5380 return FALSE;
5381
5382 sa.nLength = sizeof(sa);
5383 sa.bInheritHandle = TRUE;
5384 sa.lpSecurityDescriptor = NULL;
5385
5386 handles[0] = CreateFile(name,
5387 FILE_GENERIC_READ,
5388 FILE_SHARE_READ, &sa,
5389 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
5390
5391 if (handles[0] == INVALID_HANDLE_VALUE)
5392 {
5393 CloseHandle(handles[1]);
5394 return FALSE;
5395 }
5396
5397 return TRUE;
5398}
5399
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005400 void
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005401mch_job_start(char *cmd, job_T *job, jobopt_T *options)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005402{
5403 STARTUPINFO si;
5404 PROCESS_INFORMATION pi;
Bram Moolenaar14207f42016-10-27 21:13:10 +02005405 HANDLE jo;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005406 SECURITY_ATTRIBUTES saAttr;
5407 channel_T *channel = NULL;
Bram Moolenaard8070362016-02-15 21:56:54 +01005408 HANDLE ifd[2];
5409 HANDLE ofd[2];
5410 HANDLE efd[2];
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005411 garray_T ga;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005412
5413 int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL;
5414 int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL;
5415 int use_null_for_err = options->jo_io[PART_ERR] == JIO_NULL;
5416 int use_file_for_in = options->jo_io[PART_IN] == JIO_FILE;
5417 int use_file_for_out = options->jo_io[PART_OUT] == JIO_FILE;
5418 int use_file_for_err = options->jo_io[PART_ERR] == JIO_FILE;
5419 int use_out_for_err = options->jo_io[PART_ERR] == JIO_OUT;
5420
5421 if (use_out_for_err && use_null_for_out)
5422 use_null_for_err = TRUE;
Bram Moolenaard8070362016-02-15 21:56:54 +01005423
5424 ifd[0] = INVALID_HANDLE_VALUE;
5425 ifd[1] = INVALID_HANDLE_VALUE;
5426 ofd[0] = INVALID_HANDLE_VALUE;
5427 ofd[1] = INVALID_HANDLE_VALUE;
5428 efd[0] = INVALID_HANDLE_VALUE;
5429 efd[1] = INVALID_HANDLE_VALUE;
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005430 ga_init2(&ga, (int)sizeof(wchar_t), 500);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005431
Bram Moolenaar14207f42016-10-27 21:13:10 +02005432 jo = CreateJobObject(NULL, NULL);
5433 if (jo == NULL)
5434 {
5435 job->jv_status = JOB_FAILED;
5436 goto failed;
5437 }
5438
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005439 if (options->jo_env != NULL)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005440 win32_build_env(options->jo_env, &ga, FALSE);
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005441
Bram Moolenaar76467df2016-02-12 19:30:26 +01005442 ZeroMemory(&pi, sizeof(pi));
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005443 ZeroMemory(&si, sizeof(si));
5444 si.cb = sizeof(si);
Bram Moolenaard8070362016-02-15 21:56:54 +01005445 si.dwFlags |= STARTF_USESHOWWINDOW;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005446 si.wShowWindow = SW_HIDE;
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005447
Bram Moolenaard8070362016-02-15 21:56:54 +01005448 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
5449 saAttr.bInheritHandle = TRUE;
5450 saAttr.lpSecurityDescriptor = NULL;
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005451
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005452 if (use_file_for_in)
5453 {
5454 char_u *fname = options->jo_io_name[PART_IN];
5455
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005456 ifd[0] = job_io_file_open(fname, GENERIC_READ,
5457 FILE_SHARE_READ | FILE_SHARE_WRITE,
5458 &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL);
5459 if (ifd[0] == INVALID_HANDLE_VALUE)
Bram Moolenaar94d01912016-03-08 13:48:51 +01005460 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005461 semsg(_(e_notopen), fname);
Bram Moolenaar94d01912016-03-08 13:48:51 +01005462 goto failed;
5463 }
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005464 }
Bram Moolenaarb091f302019-01-19 14:37:00 +01005465 else if (!use_null_for_in
5466 && (!create_pipe_pair(ifd)
5467 || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005468 goto failed;
5469
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005470 if (use_file_for_out)
5471 {
5472 char_u *fname = options->jo_io_name[PART_OUT];
5473
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005474 ofd[1] = job_io_file_open(fname, GENERIC_WRITE,
5475 FILE_SHARE_READ | FILE_SHARE_WRITE,
5476 &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL);
5477 if (ofd[1] == INVALID_HANDLE_VALUE)
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005478 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005479 semsg(_(e_notopen), fname);
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005480 goto failed;
5481 }
5482 }
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005483 else if (!use_null_for_out &&
5484 (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005485 || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005486 goto failed;
5487
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005488 if (use_file_for_err)
5489 {
5490 char_u *fname = options->jo_io_name[PART_ERR];
5491
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005492 efd[1] = job_io_file_open(fname, GENERIC_WRITE,
5493 FILE_SHARE_READ | FILE_SHARE_WRITE,
5494 &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL);
5495 if (efd[1] == INVALID_HANDLE_VALUE)
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005496 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005497 semsg(_(e_notopen), fname);
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005498 goto failed;
5499 }
5500 }
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005501 else if (!use_out_for_err && !use_null_for_err &&
5502 (!CreatePipe(&efd[0], &efd[1], &saAttr, 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005503 || !SetHandleInformation(efd[0], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaard8070362016-02-15 21:56:54 +01005504 goto failed;
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005505
Bram Moolenaard8070362016-02-15 21:56:54 +01005506 si.dwFlags |= STARTF_USESTDHANDLES;
5507 si.hStdInput = ifd[0];
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005508 si.hStdOutput = ofd[1];
5509 si.hStdError = use_out_for_err ? ofd[1] : efd[1];
5510
5511 if (!use_null_for_in || !use_null_for_out || !use_null_for_err)
5512 {
Bram Moolenaarde279892016-03-11 22:19:44 +01005513 if (options->jo_set & JO_CHANNEL)
5514 {
5515 channel = options->jo_channel;
5516 if (channel != NULL)
5517 ++channel->ch_refcount;
5518 }
5519 else
5520 channel = add_channel();
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005521 if (channel == NULL)
5522 goto failed;
5523 }
Bram Moolenaard8070362016-02-15 21:56:54 +01005524
5525 if (!vim_create_process(cmd, TRUE,
Bram Moolenaar14207f42016-10-27 21:13:10 +02005526 CREATE_SUSPENDED |
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005527 CREATE_DEFAULT_ERROR_MODE |
5528 CREATE_NEW_PROCESS_GROUP |
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005529 CREATE_UNICODE_ENVIRONMENT |
Bram Moolenaar76467df2016-02-12 19:30:26 +01005530 CREATE_NEW_CONSOLE,
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005531 &si, &pi,
5532 ga.ga_data,
5533 (char *)options->jo_cwd))
Bram Moolenaar76467df2016-02-12 19:30:26 +01005534 {
Bram Moolenaar14207f42016-10-27 21:13:10 +02005535 CloseHandle(jo);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005536 job->jv_status = JOB_FAILED;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005537 goto failed;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005538 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005539
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005540 ga_clear(&ga);
5541
Bram Moolenaar14207f42016-10-27 21:13:10 +02005542 if (!AssignProcessToJobObject(jo, pi.hProcess))
5543 {
5544 /* if failing, switch the way to terminate
5545 * process with TerminateProcess. */
5546 CloseHandle(jo);
5547 jo = NULL;
5548 }
5549 ResumeThread(pi.hThread);
Bram Moolenaar75578a32016-03-10 16:33:31 +01005550 CloseHandle(pi.hThread);
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005551 job->jv_proc_info = pi;
Bram Moolenaar14207f42016-10-27 21:13:10 +02005552 job->jv_job_object = jo;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005553 job->jv_status = JOB_STARTED;
5554
Bram Moolenaar641ad6c2016-09-01 18:32:11 +02005555 CloseHandle(ifd[0]);
5556 CloseHandle(ofd[1]);
5557 if (!use_out_for_err && !use_null_for_err)
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005558 CloseHandle(efd[1]);
Bram Moolenaard8070362016-02-15 21:56:54 +01005559
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005560 job->jv_channel = channel;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005561 if (channel != NULL)
5562 {
5563 channel_set_pipes(channel,
5564 use_file_for_in || use_null_for_in
5565 ? INVALID_FD : (sock_T)ifd[1],
5566 use_file_for_out || use_null_for_out
5567 ? INVALID_FD : (sock_T)ofd[0],
5568 use_out_for_err || use_file_for_err || use_null_for_err
5569 ? INVALID_FD : (sock_T)efd[0]);
5570 channel_set_job(channel, job, options);
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005571 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005572 return;
5573
5574failed:
Bram Moolenaard8070362016-02-15 21:56:54 +01005575 CloseHandle(ifd[0]);
5576 CloseHandle(ofd[0]);
5577 CloseHandle(efd[0]);
5578 CloseHandle(ifd[1]);
5579 CloseHandle(ofd[1]);
5580 CloseHandle(efd[1]);
Bram Moolenaarde279892016-03-11 22:19:44 +01005581 channel_unref(channel);
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005582 ga_clear(&ga);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005583}
5584
5585 char *
5586mch_job_status(job_T *job)
5587{
5588 DWORD dwExitCode = 0;
5589
Bram Moolenaar76467df2016-02-12 19:30:26 +01005590 if (!GetExitCodeProcess(job->jv_proc_info.hProcess, &dwExitCode)
5591 || dwExitCode != STILL_ACTIVE)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005592 {
Bram Moolenaareab089d2016-02-21 19:32:02 +01005593 job->jv_exitval = (int)dwExitCode;
Bram Moolenaar7df915d2016-11-17 17:25:32 +01005594 if (job->jv_status < JOB_ENDED)
Bram Moolenaar97792de2016-10-15 18:36:49 +02005595 {
5596 ch_log(job->jv_channel, "Job ended");
5597 job->jv_status = JOB_ENDED;
5598 }
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005599 return "dead";
5600 }
5601 return "run";
5602}
5603
Bram Moolenaar97792de2016-10-15 18:36:49 +02005604 job_T *
5605mch_detect_ended_job(job_T *job_list)
5606{
5607 HANDLE jobHandles[MAXIMUM_WAIT_OBJECTS];
5608 job_T *jobArray[MAXIMUM_WAIT_OBJECTS];
5609 job_T *job = job_list;
5610
5611 while (job != NULL)
5612 {
5613 DWORD n;
5614 DWORD result;
5615
5616 for (n = 0; n < MAXIMUM_WAIT_OBJECTS
5617 && job != NULL; job = job->jv_next)
5618 {
5619 if (job->jv_status == JOB_STARTED)
5620 {
5621 jobHandles[n] = job->jv_proc_info.hProcess;
5622 jobArray[n] = job;
5623 ++n;
5624 }
5625 }
5626 if (n == 0)
5627 continue;
5628 result = WaitForMultipleObjects(n, jobHandles, FALSE, 0);
5629 if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + n)
5630 {
5631 job_T *wait_job = jobArray[result - WAIT_OBJECT_0];
5632
5633 if (STRCMP(mch_job_status(wait_job), "dead") == 0)
5634 return wait_job;
5635 }
5636 }
5637 return NULL;
5638}
5639
Bram Moolenaarfb630902016-10-29 14:55:00 +02005640 static BOOL
5641terminate_all(HANDLE process, int code)
5642{
5643 PROCESSENTRY32 pe;
5644 HANDLE h = INVALID_HANDLE_VALUE;
5645 DWORD pid = GetProcessId(process);
5646
5647 if (pid != 0)
5648 {
5649 h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
5650 if (h != INVALID_HANDLE_VALUE)
5651 {
5652 pe.dwSize = sizeof(PROCESSENTRY32);
5653 if (!Process32First(h, &pe))
5654 goto theend;
5655
5656 do
5657 {
5658 if (pe.th32ParentProcessID == pid)
5659 {
5660 HANDLE ph = OpenProcess(
5661 PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
5662 if (ph != NULL)
5663 {
5664 terminate_all(ph, code);
5665 CloseHandle(ph);
5666 }
5667 }
5668 } while (Process32Next(h, &pe));
5669
5670 CloseHandle(h);
5671 }
5672 }
5673
5674theend:
5675 return TerminateProcess(process, code);
5676}
5677
5678/*
5679 * Send a (deadly) signal to "job".
5680 * Return FAIL if it didn't work.
5681 */
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005682 int
Bram Moolenaar2d33e902017-08-11 16:31:54 +02005683mch_signal_job(job_T *job, char_u *how)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005684{
Bram Moolenaar923d9262016-02-25 20:56:01 +01005685 int ret;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005686
Bram Moolenaar923d9262016-02-25 20:56:01 +01005687 if (STRCMP(how, "term") == 0 || STRCMP(how, "kill") == 0 || *how == NUL)
Bram Moolenaar76467df2016-02-12 19:30:26 +01005688 {
Bram Moolenaarfb630902016-10-29 14:55:00 +02005689 /* deadly signal */
Bram Moolenaar14207f42016-10-27 21:13:10 +02005690 if (job->jv_job_object != NULL)
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01005691 {
5692 if (job->jv_channel != NULL && job->jv_channel->ch_anonymous_pipe)
5693 job->jv_channel->ch_killing = TRUE;
Bram Moolenaar14207f42016-10-27 21:13:10 +02005694 return TerminateJobObject(job->jv_job_object, 0) ? OK : FAIL;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01005695 }
Bram Moolenaarfb630902016-10-29 14:55:00 +02005696 return terminate_all(job->jv_proc_info.hProcess, 0) ? OK : FAIL;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005697 }
5698
5699 if (!AttachConsole(job->jv_proc_info.dwProcessId))
5700 return FAIL;
5701 ret = GenerateConsoleCtrlEvent(
Bram Moolenaar923d9262016-02-25 20:56:01 +01005702 STRCMP(how, "int") == 0 ? CTRL_C_EVENT : CTRL_BREAK_EVENT,
5703 job->jv_proc_info.dwProcessId)
5704 ? OK : FAIL;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005705 FreeConsole();
5706 return ret;
5707}
5708
5709/*
5710 * Clear the data related to "job".
5711 */
5712 void
5713mch_clear_job(job_T *job)
5714{
5715 if (job->jv_status != JOB_FAILED)
5716 {
Bram Moolenaar14207f42016-10-27 21:13:10 +02005717 if (job->jv_job_object != NULL)
5718 CloseHandle(job->jv_job_object);
Bram Moolenaar76467df2016-02-12 19:30:26 +01005719 CloseHandle(job->jv_proc_info.hProcess);
5720 }
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005721}
5722#endif
5723
Bram Moolenaar071d4272004-06-13 20:20:40 +00005724
5725#ifndef FEAT_GUI_W32
5726
5727/*
5728 * Start termcap mode
5729 */
5730 static void
5731termcap_mode_start(void)
5732{
5733 DWORD cmodein;
5734
5735 if (g_fTermcapMode)
5736 return;
5737
5738 SaveConsoleBuffer(&g_cbNonTermcap);
5739
5740 if (g_cbTermcap.IsValid)
5741 {
5742 /*
5743 * We've been in termcap mode before. Restore certain screen
5744 * characteristics, including the buffer size and the window
5745 * size. Since we will be redrawing the screen, we don't need
5746 * to restore the actual contents of the buffer.
5747 */
5748 RestoreConsoleBuffer(&g_cbTermcap, FALSE);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005749 reset_console_color_rgb();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005750 SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow);
5751 Rows = g_cbTermcap.Info.dwSize.Y;
5752 Columns = g_cbTermcap.Info.dwSize.X;
5753 }
5754 else
5755 {
5756 /*
5757 * This is our first time entering termcap mode. Clear the console
5758 * screen buffer, and resize the buffer to match the current window
5759 * size. We will use this as the size of our editing environment.
5760 */
5761 ClearConsoleBuffer(g_attrCurrent);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005762 set_console_color_rgb();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005763 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
5764 }
5765
5766#ifdef FEAT_TITLE
5767 resettitle();
5768#endif
5769
5770 GetConsoleMode(g_hConIn, &cmodein);
5771#ifdef FEAT_MOUSE
5772 if (g_fMouseActive)
5773 cmodein |= ENABLE_MOUSE_INPUT;
5774 else
5775 cmodein &= ~ENABLE_MOUSE_INPUT;
5776#endif
5777 cmodein |= ENABLE_WINDOW_INPUT;
5778 SetConsoleMode(g_hConIn, cmodein);
5779
5780 redraw_later_clear();
5781 g_fTermcapMode = TRUE;
5782}
5783
5784
5785/*
5786 * End termcap mode
5787 */
5788 static void
5789termcap_mode_end(void)
5790{
5791 DWORD cmodein;
5792 ConsoleBuffer *cb;
5793 COORD coord;
5794 DWORD dwDummy;
5795
5796 if (!g_fTermcapMode)
5797 return;
5798
5799 SaveConsoleBuffer(&g_cbTermcap);
5800
5801 GetConsoleMode(g_hConIn, &cmodein);
5802 cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
5803 SetConsoleMode(g_hConIn, cmodein);
5804
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01005805#ifdef FEAT_RESTORE_ORIG_SCREEN
5806 cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
5807#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005808 cb = &g_cbNonTermcap;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01005809#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005810 RestoreConsoleBuffer(cb, p_rs);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005811 reset_console_color_rgb();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005812 SetConsoleCursorInfo(g_hConOut, &g_cci);
5813
5814 if (p_rs || exiting)
5815 {
5816 /*
5817 * Clear anything that happens to be on the current line.
5818 */
5819 coord.X = 0;
5820 coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1));
5821 FillConsoleOutputCharacter(g_hConOut, ' ',
5822 cb->Info.dwSize.X, coord, &dwDummy);
5823 /*
5824 * The following is just for aesthetics. If we are exiting without
5825 * restoring the screen, then we want to have a prompt string
5826 * appear at the bottom line. However, the command interpreter
5827 * seems to always advance the cursor one line before displaying
5828 * the prompt string, which causes the screen to scroll. To
5829 * counter this, move the cursor up one line before exiting.
5830 */
5831 if (exiting && !p_rs)
5832 coord.Y--;
5833 /*
5834 * Position the cursor at the leftmost column of the desired row.
5835 */
5836 SetConsoleCursorPosition(g_hConOut, coord);
5837 }
5838
5839 g_fTermcapMode = FALSE;
5840}
5841#endif /* FEAT_GUI_W32 */
5842
5843
5844#ifdef FEAT_GUI_W32
5845 void
5846mch_write(
Bram Moolenaar1266d672017-02-01 13:43:36 +01005847 char_u *s UNUSED,
5848 int len UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005849{
5850 /* never used */
5851}
5852
5853#else
5854
5855/*
5856 * clear `n' chars, starting from `coord'
5857 */
5858 static void
5859clear_chars(
5860 COORD coord,
5861 DWORD n)
5862{
5863 DWORD dwDummy;
5864
5865 FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005866
5867 if (!USE_VTP)
5868 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy);
5869 else
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02005870 {
5871 set_console_color_rgb();
5872 gotoxy(coord.X + 1, coord.Y + 1);
5873 vtp_printf("\033[%dX", n);
5874 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005875}
5876
5877
5878/*
5879 * Clear the screen
5880 */
5881 static void
5882clear_screen(void)
5883{
5884 g_coord.X = g_coord.Y = 0;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005885
5886 if (!USE_VTP)
5887 clear_chars(g_coord, Rows * Columns);
5888 else
5889 {
5890 set_console_color_rgb();
5891 gotoxy(1, 1);
5892 vtp_printf("\033[2J");
5893 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005894}
5895
5896
5897/*
5898 * Clear to end of display
5899 */
5900 static void
5901clear_to_end_of_display(void)
5902{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005903 COORD save = g_coord;
5904
5905 if (!USE_VTP)
5906 clear_chars(g_coord, (Rows - g_coord.Y - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005907 * Columns + (Columns - g_coord.X));
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005908 else
5909 {
5910 set_console_color_rgb();
5911 gotoxy(g_coord.X + 1, g_coord.Y + 1);
5912 vtp_printf("\033[0J");
5913
5914 gotoxy(save.X + 1, save.Y + 1);
5915 g_coord = save;
5916 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005917}
5918
5919
5920/*
5921 * Clear to end of line
5922 */
5923 static void
5924clear_to_end_of_line(void)
5925{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005926 COORD save = g_coord;
5927
5928 if (!USE_VTP)
5929 clear_chars(g_coord, Columns - g_coord.X);
5930 else
5931 {
5932 set_console_color_rgb();
5933 gotoxy(g_coord.X + 1, g_coord.Y + 1);
5934 vtp_printf("\033[0K");
5935
5936 gotoxy(save.X + 1, save.Y + 1);
5937 g_coord = save;
5938 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005939}
5940
5941
5942/*
5943 * Scroll the scroll region up by `cLines' lines
5944 */
5945 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005946scroll(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005947{
5948 COORD oldcoord = g_coord;
5949
5950 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
5951 delete_lines(cLines);
5952
5953 g_coord = oldcoord;
5954}
5955
5956
5957/*
5958 * Set the scroll region
5959 */
5960 static void
5961set_scroll_region(
5962 unsigned left,
5963 unsigned top,
5964 unsigned right,
5965 unsigned bottom)
5966{
5967 if (left >= right
5968 || top >= bottom
5969 || right > (unsigned) Columns - 1
5970 || bottom > (unsigned) Rows - 1)
5971 return;
5972
5973 g_srScrollRegion.Left = left;
5974 g_srScrollRegion.Top = top;
5975 g_srScrollRegion.Right = right;
5976 g_srScrollRegion.Bottom = bottom;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005977
5978 if (USE_VTP)
5979 vtp_printf("\033[%d;%dr", top + 1, bottom + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005980}
5981
5982
5983/*
5984 * Insert `cLines' lines at the current cursor position
5985 */
5986 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005987insert_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005988{
5989 SMALL_RECT source;
5990 COORD dest;
5991 CHAR_INFO fill;
5992
5993 dest.X = 0;
5994 dest.Y = g_coord.Y + cLines;
5995
5996 source.Left = 0;
5997 source.Top = g_coord.Y;
5998 source.Right = g_srScrollRegion.Right;
5999 source.Bottom = g_srScrollRegion.Bottom - cLines;
6000
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006001 if (!USE_VTP)
6002 {
6003 fill.Char.AsciiChar = ' ';
6004 fill.Attributes = g_attrCurrent;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006005
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006006 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
6007 }
6008 else
6009 {
6010 set_console_color_rgb();
6011
6012 gotoxy(1, source.Top + 1);
6013 vtp_printf("\033[%dT", cLines);
6014 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006015
6016 /* Here we have to deal with a win32 console flake: If the scroll
6017 * region looks like abc and we scroll c to a and fill with d we get
6018 * cbd... if we scroll block c one line at a time to a, we get cdd...
6019 * vim expects cdd consistently... So we have to deal with that
6020 * here... (this also occurs scrolling the same way in the other
6021 * direction). */
6022
6023 if (source.Bottom < dest.Y)
6024 {
6025 COORD coord;
6026
6027 coord.X = 0;
6028 coord.Y = source.Bottom;
6029 clear_chars(coord, Columns * (dest.Y - source.Bottom));
6030 }
6031}
6032
6033
6034/*
6035 * Delete `cLines' lines at the current cursor position
6036 */
6037 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006038delete_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006039{
6040 SMALL_RECT source;
6041 COORD dest;
6042 CHAR_INFO fill;
6043 int nb;
6044
6045 dest.X = 0;
6046 dest.Y = g_coord.Y;
6047
6048 source.Left = 0;
6049 source.Top = g_coord.Y + cLines;
6050 source.Right = g_srScrollRegion.Right;
6051 source.Bottom = g_srScrollRegion.Bottom;
6052
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006053 if (!USE_VTP)
6054 {
6055 fill.Char.AsciiChar = ' ';
6056 fill.Attributes = g_attrCurrent;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006057
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006058 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
6059 }
6060 else
6061 {
6062 set_console_color_rgb();
6063
6064 gotoxy(1, source.Top + 1);
6065 vtp_printf("\033[%dS", cLines);
6066 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006067
6068 /* Here we have to deal with a win32 console flake: If the scroll
6069 * region looks like abc and we scroll c to a and fill with d we get
6070 * cbd... if we scroll block c one line at a time to a, we get cdd...
6071 * vim expects cdd consistently... So we have to deal with that
6072 * here... (this also occurs scrolling the same way in the other
6073 * direction). */
6074
6075 nb = dest.Y + (source.Bottom - source.Top) + 1;
6076
6077 if (nb < source.Top)
6078 {
6079 COORD coord;
6080
6081 coord.X = 0;
6082 coord.Y = nb;
6083 clear_chars(coord, Columns * (source.Top - nb));
6084 }
6085}
6086
6087
6088/*
6089 * Set the cursor position
6090 */
6091 static void
6092gotoxy(
6093 unsigned x,
6094 unsigned y)
6095{
6096 if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows)
6097 return;
6098
6099 /* external cursor coords are 1-based; internal are 0-based */
6100 g_coord.X = x - 1;
6101 g_coord.Y = y - 1;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006102
6103 if (!USE_VTP)
6104 SetConsoleCursorPosition(g_hConOut, g_coord);
6105 else
6106 vtp_printf("\033[%d;%dH", y, x);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006107}
6108
6109
6110/*
6111 * Set the current text attribute = (foreground | background)
6112 * See ../doc/os_win32.txt for the numbers.
6113 */
6114 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006115textattr(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006116{
Bram Moolenaar6383b922015-03-24 17:12:19 +01006117 g_attrCurrent = wAttr & 0xff;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006118
6119 SetConsoleTextAttribute(g_hConOut, wAttr);
6120}
6121
6122
6123 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006124textcolor(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006125{
Bram Moolenaar6383b922015-03-24 17:12:19 +01006126 g_attrCurrent = (g_attrCurrent & 0xf0) + (wAttr & 0x0f);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006127
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006128 if (!USE_VTP)
6129 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
6130 else
6131 vtp_sgr_bulk(wAttr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006132}
6133
6134
6135 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006136textbackground(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006137{
Bram Moolenaar6383b922015-03-24 17:12:19 +01006138 g_attrCurrent = (g_attrCurrent & 0x0f) + ((wAttr & 0x0f) << 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006139
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006140 if (!USE_VTP)
6141 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
6142 else
6143 vtp_sgr_bulk(wAttr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006144}
6145
6146
6147/*
6148 * restore the default text attribute (whatever we started with)
6149 */
6150 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006151normvideo(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006152{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006153 if (!USE_VTP)
6154 textattr(g_attrDefault);
6155 else
6156 vtp_sgr_bulk(0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006157}
6158
6159
6160static WORD g_attrPreStandout = 0;
6161
6162/*
6163 * Make the text standout, by brightening it
6164 */
6165 static void
6166standout(void)
6167{
6168 g_attrPreStandout = g_attrCurrent;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006169
Bram Moolenaar071d4272004-06-13 20:20:40 +00006170 textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
6171}
6172
6173
6174/*
6175 * Turn off standout mode
6176 */
6177 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006178standend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006179{
6180 if (g_attrPreStandout)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006181 textattr(g_attrPreStandout);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006182
6183 g_attrPreStandout = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006184}
6185
6186
6187/*
Bram Moolenaarff1d0d42007-05-10 17:24:16 +00006188 * Set normal fg/bg color, based on T_ME. Called when t_me has been set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006189 */
6190 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006191mch_set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006192{
6193 char_u *p;
6194 int n;
6195
6196 cterm_normal_fg_color = (g_attrDefault & 0xf) + 1;
6197 cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006198 if (
6199#ifdef FEAT_TERMGUICOLORS
6200 !p_tgc &&
6201#endif
6202 T_ME[0] == ESC && T_ME[1] == '|')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006203 {
6204 p = T_ME + 2;
6205 n = getdigits(&p);
6206 if (*p == 'm' && n > 0)
6207 {
6208 cterm_normal_fg_color = (n & 0xf) + 1;
6209 cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
6210 }
6211 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006212#ifdef FEAT_TERMGUICOLORS
6213 cterm_normal_fg_gui_color = INVALCOLOR;
6214 cterm_normal_bg_gui_color = INVALCOLOR;
6215#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006216}
6217
6218
6219/*
6220 * visual bell: flash the screen
6221 */
6222 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006223visual_bell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006224{
6225 COORD coordOrigin = {0, 0};
6226 WORD attrFlash = ~g_attrCurrent & 0xff;
6227
6228 DWORD dwDummy;
6229 LPWORD oldattrs = (LPWORD)alloc(Rows * Columns * sizeof(WORD));
6230
6231 if (oldattrs == NULL)
6232 return;
6233 ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
6234 coordOrigin, &dwDummy);
6235 FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns,
6236 coordOrigin, &dwDummy);
6237
6238 Sleep(15); /* wait for 15 msec */
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006239 if (!USE_VTP)
6240 WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006241 coordOrigin, &dwDummy);
6242 vim_free(oldattrs);
6243}
6244
6245
6246/*
6247 * Make the cursor visible or invisible
6248 */
6249 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006250cursor_visible(BOOL fVisible)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006251{
6252 s_cursor_visible = fVisible;
6253#ifdef MCH_CURSOR_SHAPE
6254 mch_update_cursor();
6255#endif
6256}
6257
6258
6259/*
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006260 * Write "cbToWrite" bytes in `pchBuf' to the screen.
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006261 * Returns the number of bytes actually written (at least one).
Bram Moolenaar071d4272004-06-13 20:20:40 +00006262 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006263 static DWORD
Bram Moolenaar071d4272004-06-13 20:20:40 +00006264write_chars(
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006265 char_u *pchBuf,
6266 DWORD cbToWrite)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006267{
6268 COORD coord = g_coord;
6269 DWORD written;
6270
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006271 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6272 {
6273 static WCHAR *unicodebuf = NULL;
6274 static int unibuflen = 0;
6275 int length;
6276 DWORD n, cchwritten, cells;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006277
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006278 length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite, 0, 0);
6279 if (unicodebuf == NULL || length > unibuflen)
6280 {
6281 vim_free(unicodebuf);
6282 unicodebuf = (WCHAR *)lalloc(length * sizeof(WCHAR), FALSE);
6283 unibuflen = length;
6284 }
6285 MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite,
6286 unicodebuf, unibuflen);
6287
6288 cells = mb_string2cells(pchBuf, cbToWrite);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006289
6290 if (!USE_VTP)
6291 {
6292 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cells,
6293 coord, &written);
6294 /* When writing fails or didn't write a single character, pretend one
6295 * character was written, otherwise we get stuck. */
6296 if (WriteConsoleOutputCharacterW(g_hConOut, unicodebuf, length,
6297 coord, &cchwritten) == 0
6298 || cchwritten == 0)
6299 cchwritten = 1;
6300 }
6301 else
6302 {
6303 if (WriteConsoleW(g_hConOut, unicodebuf, length, &cchwritten,
6304 NULL) == 0 || cchwritten == 0)
6305 cchwritten = 1;
6306 }
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006307
6308 if (cchwritten == length)
6309 {
6310 written = cbToWrite;
6311 g_coord.X += (SHORT)cells;
6312 }
6313 else
6314 {
6315 char_u *p = pchBuf;
6316 for (n = 0; n < cchwritten; n++)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01006317 MB_CPTR_ADV(p);
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006318 written = p - pchBuf;
6319 g_coord.X += (SHORT)mb_string2cells(pchBuf, written);
6320 }
6321 }
6322 else
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006323 {
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006324 if (!USE_VTP)
6325 {
6326 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cbToWrite,
6327 coord, &written);
6328 /* When writing fails or didn't write a single character, pretend one
6329 * character was written, otherwise we get stuck. */
6330 if (WriteConsoleOutputCharacter(g_hConOut, (LPCSTR)pchBuf, cbToWrite,
6331 coord, &written) == 0
6332 || written == 0)
6333 written = 1;
6334 }
6335 else
6336 {
6337 if (WriteConsole(g_hConOut, (LPCSTR)pchBuf, cbToWrite, &written,
6338 NULL) == 0 || written == 0)
6339 written = 1;
6340 }
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006341
6342 g_coord.X += (SHORT) written;
6343 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006344
6345 while (g_coord.X > g_srScrollRegion.Right)
6346 {
6347 g_coord.X -= (SHORT) Columns;
6348 if (g_coord.Y < g_srScrollRegion.Bottom)
6349 g_coord.Y++;
6350 }
6351
6352 gotoxy(g_coord.X + 1, g_coord.Y + 1);
6353
6354 return written;
6355}
6356
6357
6358/*
6359 * mch_write(): write the output buffer to the screen, translating ESC
6360 * sequences into calls to console output routines.
6361 */
6362 void
6363mch_write(
6364 char_u *s,
6365 int len)
6366{
6367 s[len] = NUL;
6368
6369 if (!term_console)
6370 {
6371 write(1, s, (unsigned)len);
6372 return;
6373 }
6374
6375 /* translate ESC | sequences into faked bios calls */
6376 while (len--)
6377 {
6378 /* optimization: use one single write_chars for runs of text,
6379 * rather than once per character It ain't curses, but it helps. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006380 DWORD prefix = (DWORD)strcspn((char *)s, "\n\r\b\a\033");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006381
6382 if (p_wd)
6383 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006384 WaitForChar(p_wd, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006385 if (prefix != 0)
6386 prefix = 1;
6387 }
6388
6389 if (prefix != 0)
6390 {
6391 DWORD nWritten;
6392
6393 nWritten = write_chars(s, prefix);
6394#ifdef MCH_WRITE_DUMP
6395 if (fdDump)
6396 {
6397 fputc('>', fdDump);
6398 fwrite(s, sizeof(char_u), nWritten, fdDump);
6399 fputs("<\n", fdDump);
6400 }
6401#endif
6402 len -= (nWritten - 1);
6403 s += nWritten;
6404 }
6405 else if (s[0] == '\n')
6406 {
6407 /* \n, newline: go to the beginning of the next line or scroll */
6408 if (g_coord.Y == g_srScrollRegion.Bottom)
6409 {
6410 scroll(1);
6411 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
6412 }
6413 else
6414 {
6415 gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
6416 }
6417#ifdef MCH_WRITE_DUMP
6418 if (fdDump)
6419 fputs("\\n\n", fdDump);
6420#endif
6421 s++;
6422 }
6423 else if (s[0] == '\r')
6424 {
6425 /* \r, carriage return: go to beginning of line */
6426 gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
6427#ifdef MCH_WRITE_DUMP
6428 if (fdDump)
6429 fputs("\\r\n", fdDump);
6430#endif
6431 s++;
6432 }
6433 else if (s[0] == '\b')
6434 {
6435 /* \b, backspace: move cursor one position left */
6436 if (g_coord.X > g_srScrollRegion.Left)
6437 g_coord.X--;
6438 else if (g_coord.Y > g_srScrollRegion.Top)
6439 {
6440 g_coord.X = g_srScrollRegion.Right;
6441 g_coord.Y--;
6442 }
6443 gotoxy(g_coord.X + 1, g_coord.Y + 1);
6444#ifdef MCH_WRITE_DUMP
6445 if (fdDump)
6446 fputs("\\b\n", fdDump);
6447#endif
6448 s++;
6449 }
6450 else if (s[0] == '\a')
6451 {
6452 /* \a, bell */
6453 MessageBeep(0xFFFFFFFF);
6454#ifdef MCH_WRITE_DUMP
6455 if (fdDump)
6456 fputs("\\a\n", fdDump);
6457#endif
6458 s++;
6459 }
6460 else if (s[0] == ESC && len >= 3-1 && s[1] == '|')
6461 {
6462#ifdef MCH_WRITE_DUMP
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006463 char_u *old_s = s;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006464#endif
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006465 char_u *p;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006466 int arg1 = 0, arg2 = 0, argc = 0, args[16];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006467
6468 switch (s[2])
6469 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006470 case '0': case '1': case '2': case '3': case '4':
6471 case '5': case '6': case '7': case '8': case '9':
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006472 p = s + 1;
6473 do
6474 {
6475 ++p;
6476 args[argc] = getdigits(&p);
6477 argc += (argc < 15) ? 1 : 0;
6478 if (p > s + len)
6479 break;
6480 } while (*p == ';');
6481
Bram Moolenaar071d4272004-06-13 20:20:40 +00006482 if (p > s + len)
6483 break;
6484
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006485 arg1 = args[0];
6486 arg2 = args[1];
6487 if (*p == 'm')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006488 {
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006489 if (argc == 1 && args[0] == 0)
6490 normvideo();
6491 else if (argc == 1)
6492 {
6493 if (USE_VTP)
6494 textcolor((WORD) arg1);
6495 else
6496 textattr((WORD) arg1);
6497 }
6498 else if (USE_VTP)
6499 vtp_sgr_bulks(argc, args);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006500 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006501 else if (argc == 2 && *p == 'H')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006502 {
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006503 gotoxy(arg2, arg1);
6504 }
6505 else if (argc == 2 && *p == 'r')
6506 {
6507 set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
6508 }
6509 else if (argc == 1 && *p == 'A')
6510 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006511 gotoxy(g_coord.X + 1,
6512 max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
6513 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006514 else if (argc == 1 && *p == 'b')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006515 {
6516 textbackground((WORD) arg1);
6517 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006518 else if (argc == 1 && *p == 'C')
6519 {
6520 gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
6521 g_coord.Y + 1);
6522 }
6523 else if (argc == 1 && *p == 'f')
6524 {
6525 textcolor((WORD) arg1);
6526 }
6527 else if (argc == 1 && *p == 'H')
6528 {
6529 gotoxy(1, arg1);
6530 }
6531 else if (argc == 1 && *p == 'L')
6532 {
6533 insert_lines(arg1);
6534 }
6535 else if (argc == 1 && *p == 'M')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006536 {
6537 delete_lines(arg1);
6538 }
6539
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00006540 len -= (int)(p - s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006541 s = p + 1;
6542 break;
6543
Bram Moolenaar071d4272004-06-13 20:20:40 +00006544 case 'A':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006545 gotoxy(g_coord.X + 1,
6546 max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
6547 goto got3;
6548
6549 case 'B':
6550 visual_bell();
6551 goto got3;
6552
6553 case 'C':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006554 gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
6555 g_coord.Y + 1);
6556 goto got3;
6557
6558 case 'E':
6559 termcap_mode_end();
6560 goto got3;
6561
6562 case 'F':
6563 standout();
6564 goto got3;
6565
6566 case 'f':
6567 standend();
6568 goto got3;
6569
6570 case 'H':
6571 gotoxy(1, 1);
6572 goto got3;
6573
6574 case 'j':
6575 clear_to_end_of_display();
6576 goto got3;
6577
6578 case 'J':
6579 clear_screen();
6580 goto got3;
6581
6582 case 'K':
6583 clear_to_end_of_line();
6584 goto got3;
6585
6586 case 'L':
6587 insert_lines(1);
6588 goto got3;
6589
6590 case 'M':
6591 delete_lines(1);
6592 goto got3;
6593
6594 case 'S':
6595 termcap_mode_start();
6596 goto got3;
6597
6598 case 'V':
6599 cursor_visible(TRUE);
6600 goto got3;
6601
6602 case 'v':
6603 cursor_visible(FALSE);
6604 goto got3;
6605
6606 got3:
6607 s += 3;
6608 len -= 2;
6609 }
6610
6611#ifdef MCH_WRITE_DUMP
6612 if (fdDump)
6613 {
6614 fputs("ESC | ", fdDump);
6615 fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
6616 fputc('\n', fdDump);
6617 }
6618#endif
6619 }
6620 else
6621 {
6622 /* Write a single character */
6623 DWORD nWritten;
6624
6625 nWritten = write_chars(s, 1);
6626#ifdef MCH_WRITE_DUMP
6627 if (fdDump)
6628 {
6629 fputc('>', fdDump);
6630 fwrite(s, sizeof(char_u), nWritten, fdDump);
6631 fputs("<\n", fdDump);
6632 }
6633#endif
6634
6635 len -= (nWritten - 1);
6636 s += nWritten;
6637 }
6638 }
6639
6640#ifdef MCH_WRITE_DUMP
6641 if (fdDump)
6642 fflush(fdDump);
6643#endif
6644}
6645
6646#endif /* FEAT_GUI_W32 */
6647
6648
6649/*
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01006650 * Delay for "msec" milliseconds.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006651 */
6652 void
6653mch_delay(
6654 long msec,
Bram Moolenaar1266d672017-02-01 13:43:36 +01006655 int ignoreinput UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006656{
6657#ifdef FEAT_GUI_W32
6658 Sleep((int)msec); /* never wait for input */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006659#else /* Console */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006660 if (ignoreinput)
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006661# ifdef FEAT_MZSCHEME
6662 if (mzthreads_allowed() && p_mzq > 0 && msec > p_mzq)
6663 {
6664 int towait = p_mzq;
6665
6666 /* if msec is large enough, wait by portions in p_mzq */
6667 while (msec > 0)
6668 {
6669 mzvim_check_threads();
6670 if (msec < towait)
6671 towait = msec;
6672 Sleep(towait);
6673 msec -= towait;
6674 }
6675 }
6676 else
6677# endif
6678 Sleep((int)msec);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006679 else
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006680 WaitForChar(msec, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006681#endif
6682}
6683
6684
6685/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01006686 * This version of remove is not scared by a readonly (backup) file.
6687 * This can also remove a symbolic link like Unix.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006688 * Return 0 for success, -1 for failure.
6689 */
6690 int
6691mch_remove(char_u *name)
6692{
Bram Moolenaar071d4272004-06-13 20:20:40 +00006693 WCHAR *wn = NULL;
6694 int n;
6695
Bram Moolenaar203258c2016-01-17 22:15:16 +01006696 /*
6697 * On Windows, deleting a directory's symbolic link is done by
6698 * RemoveDirectory(): mch_rmdir. It seems unnatural, but it is fact.
6699 */
6700 if (mch_isdir(name) && mch_is_symbolic_link(name))
6701 return mch_rmdir(name);
6702
Bram Moolenaar12b559e2013-06-12 22:41:37 +02006703 win32_setattrs(name, FILE_ATTRIBUTE_NORMAL);
6704
Bram Moolenaar071d4272004-06-13 20:20:40 +00006705 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6706 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006707 wn = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006708 if (wn != NULL)
6709 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006710 n = DeleteFileW(wn) ? 0 : -1;
6711 vim_free(wn);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006712 return n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006713 }
6714 }
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006715 return DeleteFile((LPCSTR)name) ? 0 : -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006716}
6717
6718
6719/*
Bram Moolenaarb9c31e72016-09-29 15:18:57 +02006720 * Check for an "interrupt signal": CTRL-break or CTRL-C.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006721 */
6722 void
Bram Moolenaarb9c31e72016-09-29 15:18:57 +02006723mch_breakcheck(int force)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006724{
6725#ifndef FEAT_GUI_W32 /* never used */
6726 if (g_fCtrlCPressed || g_fCBrkPressed)
6727 {
Bram Moolenaar9698ad72017-08-12 14:52:15 +02006728 ctrl_break_was_pressed = g_fCBrkPressed;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006729 g_fCtrlCPressed = g_fCBrkPressed = FALSE;
6730 got_int = TRUE;
6731 }
6732#endif
6733}
6734
Bram Moolenaaree273972016-01-02 21:11:51 +01006735/* physical RAM to leave for the OS */
6736#define WINNT_RESERVE_BYTES (256*1024*1024)
Bram Moolenaaree273972016-01-02 21:11:51 +01006737
6738/*
6739 * How much main memory in KiB that can be used by VIM.
6740 */
Bram Moolenaaree273972016-01-02 21:11:51 +01006741 long_u
Bram Moolenaar1266d672017-02-01 13:43:36 +01006742mch_total_mem(int special UNUSED)
Bram Moolenaaree273972016-01-02 21:11:51 +01006743{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006744 MEMORYSTATUSEX ms;
6745
Bram Moolenaaree273972016-01-02 21:11:51 +01006746 PlatformId();
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006747 /* Need to use GlobalMemoryStatusEx() when there is more memory than
6748 * what fits in 32 bits. But it's not always available. */
6749 ms.dwLength = sizeof(MEMORYSTATUSEX);
6750 GlobalMemoryStatusEx(&ms);
6751 if (ms.ullAvailVirtual < ms.ullTotalPhys)
Bram Moolenaaree273972016-01-02 21:11:51 +01006752 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006753 /* Process address space fits in physical RAM, use all of it. */
6754 return (long_u)(ms.ullAvailVirtual / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01006755 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006756 if (ms.ullTotalPhys <= WINNT_RESERVE_BYTES)
Bram Moolenaaree273972016-01-02 21:11:51 +01006757 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006758 /* Catch old NT box or perverse hardware setup. */
6759 return (long_u)((ms.ullTotalPhys / 2) / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01006760 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006761 /* Use physical RAM less reserve for OS + data. */
6762 return (long_u)((ms.ullTotalPhys - WINNT_RESERVE_BYTES) / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01006763}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006764
Bram Moolenaar071d4272004-06-13 20:20:40 +00006765/*
6766 * Same code as below, but with wide functions and no comments.
6767 * Return 0 for success, non-zero for failure.
6768 */
6769 int
6770mch_wrename(WCHAR *wold, WCHAR *wnew)
6771{
6772 WCHAR *p;
6773 int i;
6774 WCHAR szTempFile[_MAX_PATH + 1];
6775 WCHAR szNewPath[_MAX_PATH + 1];
6776 HANDLE hf;
6777
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006778 p = wold;
6779 for (i = 0; wold[i] != NUL; ++i)
6780 if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':')
6781 && wold[i + 1] != 0)
6782 p = wold + i + 1;
6783 if ((int)(wold + i - p) < 8 || p[6] != '~')
6784 return (MoveFileW(wold, wnew) == 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006785
6786 if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL)
6787 return -1;
6788 *p = NUL;
6789
6790 if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0)
6791 return -2;
6792
6793 if (!DeleteFileW(szTempFile))
6794 return -3;
6795
6796 if (!MoveFileW(wold, szTempFile))
6797 return -4;
6798
6799 if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW,
6800 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
6801 return -5;
6802 if (!CloseHandle(hf))
6803 return -6;
6804
6805 if (!MoveFileW(szTempFile, wnew))
6806 {
6807 (void)MoveFileW(szTempFile, wold);
6808 return -7;
6809 }
6810
6811 DeleteFileW(szTempFile);
6812
6813 if (!DeleteFileW(wold))
6814 return -8;
6815
6816 return 0;
6817}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006818
6819
6820/*
6821 * mch_rename() works around a bug in rename (aka MoveFile) in
6822 * Windows 95: rename("foo.bar", "foo.bar~") will generate a
6823 * file whose short file name is "FOO.BAR" (its long file name will
6824 * be correct: "foo.bar~"). Because a file can be accessed by
6825 * either its SFN or its LFN, "foo.bar" has effectively been
6826 * renamed to "foo.bar", which is not at all what was wanted. This
6827 * seems to happen only when renaming files with three-character
6828 * extensions by appending a suffix that does not include ".".
6829 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
6830 *
6831 * There is another problem, which isn't really a bug but isn't right either:
6832 * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be
6833 * "abcdef~1.txt" again. This has been reported on Windows NT 4.0 with
6834 * service pack 6. Doesn't seem to happen on Windows 98.
6835 *
6836 * Like rename(), returns 0 upon success, non-zero upon failure.
6837 * Should probably set errno appropriately when errors occur.
6838 */
6839 int
6840mch_rename(
6841 const char *pszOldFile,
6842 const char *pszNewFile)
6843{
6844 char szTempFile[_MAX_PATH+1];
6845 char szNewPath[_MAX_PATH+1];
6846 char *pszFilePart;
6847 HANDLE hf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006848 WCHAR *wold = NULL;
6849 WCHAR *wnew = NULL;
6850 int retval = -1;
6851
6852 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6853 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006854 wold = enc_to_utf16((char_u *)pszOldFile, NULL);
6855 wnew = enc_to_utf16((char_u *)pszNewFile, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006856 if (wold != NULL && wnew != NULL)
6857 retval = mch_wrename(wold, wnew);
6858 vim_free(wold);
6859 vim_free(wnew);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006860 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006861 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006862
6863 /*
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006864 * No need to play tricks unless the file name contains a "~" as the
6865 * seventh character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006866 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006867 pszFilePart = (char *)gettail((char_u *)pszOldFile);
6868 if (STRLEN(pszFilePart) < 8 || pszFilePart[6] != '~')
6869 return rename(pszOldFile, pszNewFile);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006870
6871 /* Get base path of new file name. Undocumented feature: If pszNewFile is
6872 * a directory, no error is returned and pszFilePart will be NULL. */
6873 if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0
6874 || pszFilePart == NULL)
6875 return -1;
6876 *pszFilePart = NUL;
6877
6878 /* Get (and create) a unique temporary file name in directory of new file */
6879 if (GetTempFileName(szNewPath, "VIM", 0, szTempFile) == 0)
6880 return -2;
6881
6882 /* blow the temp file away */
6883 if (!DeleteFile(szTempFile))
6884 return -3;
6885
6886 /* rename old file to the temp file */
6887 if (!MoveFile(pszOldFile, szTempFile))
6888 return -4;
6889
6890 /* now create an empty file called pszOldFile; this prevents the operating
6891 * system using pszOldFile as an alias (SFN) if we're renaming within the
6892 * same directory. For example, we're editing a file called
6893 * filename.asc.txt by its SFN, filena~1.txt. If we rename filena~1.txt
6894 * to filena~1.txt~ (i.e., we're making a backup while writing it), the
6895 * SFN for filena~1.txt~ will be filena~1.txt, by default, which will
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006896 * cause all sorts of problems later in buf_write(). So, we create an
6897 * empty file called filena~1.txt and the system will have to find some
6898 * other SFN for filena~1.txt~, such as filena~2.txt
Bram Moolenaar071d4272004-06-13 20:20:40 +00006899 */
6900 if ((hf = CreateFile(pszOldFile, GENERIC_WRITE, 0, NULL, CREATE_NEW,
6901 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
6902 return -5;
6903 if (!CloseHandle(hf))
6904 return -6;
6905
6906 /* rename the temp file to the new file */
6907 if (!MoveFile(szTempFile, pszNewFile))
6908 {
6909 /* Renaming failed. Rename the file back to its old name, so that it
6910 * looks like nothing happened. */
6911 (void)MoveFile(szTempFile, pszOldFile);
6912
6913 return -7;
6914 }
6915
6916 /* Seems to be left around on Novell filesystems */
6917 DeleteFile(szTempFile);
6918
6919 /* finally, remove the empty old file */
6920 if (!DeleteFile(pszOldFile))
6921 return -8;
6922
6923 return 0; /* success */
6924}
6925
6926/*
6927 * Get the default shell for the current hardware platform
6928 */
6929 char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006930default_shell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006931{
Bram Moolenaar071d4272004-06-13 20:20:40 +00006932 PlatformId();
6933
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006934 return "cmd.exe";
Bram Moolenaar071d4272004-06-13 20:20:40 +00006935}
6936
6937/*
6938 * mch_access() extends access() to do more detailed check on network drives.
6939 * Returns 0 if file "n" has access rights according to "p", -1 otherwise.
6940 */
6941 int
6942mch_access(char *n, int p)
6943{
6944 HANDLE hFile;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006945 int retval = -1; /* default: fail */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006946 WCHAR *wn = NULL;
6947
6948 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006949 wn = enc_to_utf16((char_u *)n, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006950
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006951 if (mch_isdir((char_u *)n))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006952 {
6953 char TempName[_MAX_PATH + 16] = "";
Bram Moolenaar071d4272004-06-13 20:20:40 +00006954 WCHAR TempNameW[_MAX_PATH + 16] = L"";
Bram Moolenaar071d4272004-06-13 20:20:40 +00006955
6956 if (p & R_OK)
6957 {
6958 /* Read check is performed by seeing if we can do a find file on
6959 * the directory for any file. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006960 if (wn != NULL)
6961 {
6962 int i;
6963 WIN32_FIND_DATAW d;
6964
6965 for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i)
6966 TempNameW[i] = wn[i];
6967 if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/')
6968 TempNameW[i++] = '\\';
6969 TempNameW[i++] = '*';
6970 TempNameW[i++] = 0;
6971
6972 hFile = FindFirstFileW(TempNameW, &d);
6973 if (hFile == INVALID_HANDLE_VALUE)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006974 goto getout;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006975 else
6976 (void)FindClose(hFile);
6977 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006978 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006979 {
6980 char *pch;
6981 WIN32_FIND_DATA d;
6982
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006983 vim_strncpy((char_u *)TempName, (char_u *)n, _MAX_PATH);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006984 pch = TempName + STRLEN(TempName) - 1;
6985 if (*pch != '\\' && *pch != '/')
6986 *++pch = '\\';
6987 *++pch = '*';
6988 *++pch = NUL;
6989
6990 hFile = FindFirstFile(TempName, &d);
6991 if (hFile == INVALID_HANDLE_VALUE)
6992 goto getout;
6993 (void)FindClose(hFile);
6994 }
6995 }
6996
6997 if (p & W_OK)
6998 {
6999 /* Trying to create a temporary file in the directory should catch
7000 * directories on read-only network shares. However, in
7001 * directories whose ACL allows writes but denies deletes will end
7002 * up keeping the temporary file :-(. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007003 if (wn != NULL)
7004 {
7005 if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW))
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007006 goto getout;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007007 else
7008 DeleteFileW(TempNameW);
7009 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007010 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007011 {
7012 if (!GetTempFileName(n, "VIM", 0, TempName))
7013 goto getout;
7014 mch_remove((char_u *)TempName);
7015 }
7016 }
7017 }
7018 else
7019 {
Bram Moolenaar5aa98962018-05-06 17:09:38 +02007020 // Don't consider a file read-only if another process has opened it.
7021 DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7022
Bram Moolenaar071d4272004-06-13 20:20:40 +00007023 /* Trying to open the file for the required access does ACL, read-only
7024 * network share, and file attribute checks. */
Bram Moolenaar5aa98962018-05-06 17:09:38 +02007025 DWORD access_mode = ((p & W_OK) ? GENERIC_WRITE : 0)
7026 | ((p & R_OK) ? GENERIC_READ : 0);
7027
Bram Moolenaar071d4272004-06-13 20:20:40 +00007028 if (wn != NULL)
Bram Moolenaar5aa98962018-05-06 17:09:38 +02007029 hFile = CreateFileW(wn, access_mode, share_mode,
7030 NULL, OPEN_EXISTING, 0, NULL);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007031 else
Bram Moolenaar5aa98962018-05-06 17:09:38 +02007032 hFile = CreateFile(n, access_mode, share_mode,
7033 NULL, OPEN_EXISTING, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007034 if (hFile == INVALID_HANDLE_VALUE)
7035 goto getout;
7036 CloseHandle(hFile);
7037 }
7038
7039 retval = 0; /* success */
7040getout:
Bram Moolenaar071d4272004-06-13 20:20:40 +00007041 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007042 return retval;
7043}
7044
Bram Moolenaar071d4272004-06-13 20:20:40 +00007045/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007046 * Version of open() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007047 */
7048 int
Bram Moolenaarb6843a02017-08-02 22:07:12 +02007049mch_open(const char *name, int flags, int mode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007050{
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00007051 /* _wopen() does not work with Borland C 5.5: creates a read-only file. */
Bram Moolenaara12a1612019-01-24 16:39:02 +01007052#ifndef __BORLANDC__
Bram Moolenaar071d4272004-06-13 20:20:40 +00007053 WCHAR *wn;
7054 int f;
7055
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00007056 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007057 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01007058 wn = enc_to_utf16((char_u *)name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007059 if (wn != NULL)
7060 {
7061 f = _wopen(wn, flags, mode);
7062 vim_free(wn);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007063 return f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007064 }
7065 }
Bram Moolenaara12a1612019-01-24 16:39:02 +01007066#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007067
Bram Moolenaarf9e6c3b2014-11-05 18:36:03 +01007068 /* open() can open a file which name is longer than _MAX_PATH bytes
7069 * and shorter than _MAX_PATH characters successfully, but sometimes it
7070 * causes unexpected error in another part. We make it an error explicitly
7071 * here. */
7072 if (strlen(name) >= _MAX_PATH)
7073 return -1;
7074
Bram Moolenaar071d4272004-06-13 20:20:40 +00007075 return open(name, flags, mode);
7076}
7077
7078/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007079 * Version of fopen() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007080 */
7081 FILE *
Bram Moolenaarb6843a02017-08-02 22:07:12 +02007082mch_fopen(const char *name, const char *mode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007083{
7084 WCHAR *wn, *wm;
7085 FILE *f = NULL;
7086
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007087 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007088 {
Bram Moolenaara12a1612019-01-24 16:39:02 +01007089#if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0fde2902008-03-16 13:54:13 +00007090 /* Work around an annoying assertion in the Microsoft debug CRT
7091 * when mode's text/binary setting doesn't match _get_fmode(). */
7092 char newMode = mode[strlen(mode) - 1];
7093 int oldMode = 0;
7094
7095 _get_fmode(&oldMode);
7096 if (newMode == 't')
7097 _set_fmode(_O_TEXT);
7098 else if (newMode == 'b')
7099 _set_fmode(_O_BINARY);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007100#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01007101 wn = enc_to_utf16((char_u *)name, NULL);
7102 wm = enc_to_utf16((char_u *)mode, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007103 if (wn != NULL && wm != NULL)
7104 f = _wfopen(wn, wm);
7105 vim_free(wn);
7106 vim_free(wm);
Bram Moolenaar0fde2902008-03-16 13:54:13 +00007107
Bram Moolenaara12a1612019-01-24 16:39:02 +01007108#if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0fde2902008-03-16 13:54:13 +00007109 _set_fmode(oldMode);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007110#endif
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007111 return f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007112 }
7113
Bram Moolenaarf9e6c3b2014-11-05 18:36:03 +01007114 /* fopen() can open a file which name is longer than _MAX_PATH bytes
7115 * and shorter than _MAX_PATH characters successfully, but sometimes it
7116 * causes unexpected error in another part. We make it an error explicitly
7117 * here. */
7118 if (strlen(name) >= _MAX_PATH)
7119 return NULL;
7120
Bram Moolenaar071d4272004-06-13 20:20:40 +00007121 return fopen(name, mode);
7122}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007123
Bram Moolenaar071d4272004-06-13 20:20:40 +00007124/*
7125 * SUB STREAM (aka info stream) handling:
7126 *
7127 * NTFS can have sub streams for each file. Normal contents of file is
7128 * stored in the main stream, and extra contents (author information and
7129 * title and so on) can be stored in sub stream. After Windows 2000, user
7130 * can access and store those informations in sub streams via explorer's
7131 * property menuitem in right click menu. Those informations in sub streams
7132 * were lost when copying only the main stream. So we have to copy sub
7133 * streams.
7134 *
7135 * Incomplete explanation:
7136 * http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp
7137 * More useful info and an example:
7138 * http://www.sysinternals.com/ntw2k/source/misc.shtml#streams
7139 */
7140
7141/*
7142 * Copy info stream data "substream". Read from the file with BackupRead(sh)
7143 * and write to stream "substream" of file "to".
7144 * Errors are ignored.
7145 */
7146 static void
7147copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len)
7148{
7149 HANDLE hTo;
7150 WCHAR *to_name;
7151
7152 to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR));
7153 wcscpy(to_name, to);
7154 wcscat(to_name, substream);
7155
7156 hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
7157 FILE_ATTRIBUTE_NORMAL, NULL);
7158 if (hTo != INVALID_HANDLE_VALUE)
7159 {
7160 long done;
7161 DWORD todo;
7162 DWORD readcnt, written;
7163 char buf[4096];
7164
7165 /* Copy block of bytes at a time. Abort when something goes wrong. */
7166 for (done = 0; done < len; done += written)
7167 {
7168 /* (size_t) cast for Borland C 5.5 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007169 todo = (DWORD)((size_t)(len - done) > sizeof(buf) ? sizeof(buf)
7170 : (size_t)(len - done));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007171 if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt,
7172 FALSE, FALSE, context)
7173 || readcnt != todo
7174 || !WriteFile(hTo, buf, todo, &written, NULL)
7175 || written != todo)
7176 break;
7177 }
7178 CloseHandle(hTo);
7179 }
7180
7181 free(to_name);
7182}
7183
7184/*
7185 * Copy info streams from file "from" to file "to".
7186 */
7187 static void
7188copy_infostreams(char_u *from, char_u *to)
7189{
7190 WCHAR *fromw;
7191 WCHAR *tow;
7192 HANDLE sh;
7193 WIN32_STREAM_ID sid;
7194 int headersize;
7195 WCHAR streamname[_MAX_PATH];
7196 DWORD readcount;
7197 void *context = NULL;
7198 DWORD lo, hi;
7199 int len;
7200
7201 /* Convert the file names to wide characters. */
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007202 fromw = enc_to_utf16(from, NULL);
7203 tow = enc_to_utf16(to, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007204 if (fromw != NULL && tow != NULL)
7205 {
7206 /* Open the file for reading. */
7207 sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL,
7208 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
7209 if (sh != INVALID_HANDLE_VALUE)
7210 {
7211 /* Use BackupRead() to find the info streams. Repeat until we
7212 * have done them all.*/
7213 for (;;)
7214 {
7215 /* Get the header to find the length of the stream name. If
7216 * the "readcount" is zero we have done all info streams. */
7217 ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007218 headersize = (int)((char *)&sid.cStreamName - (char *)&sid.dwStreamId);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007219 if (!BackupRead(sh, (LPBYTE)&sid, headersize,
7220 &readcount, FALSE, FALSE, &context)
7221 || readcount == 0)
7222 break;
7223
7224 /* We only deal with streams that have a name. The normal
7225 * file data appears to be without a name, even though docs
7226 * suggest it is called "::$DATA". */
7227 if (sid.dwStreamNameSize > 0)
7228 {
7229 /* Read the stream name. */
7230 if (!BackupRead(sh, (LPBYTE)streamname,
7231 sid.dwStreamNameSize,
7232 &readcount, FALSE, FALSE, &context))
7233 break;
7234
7235 /* Copy an info stream with a name ":anything:$DATA".
7236 * Skip "::$DATA", it has no stream name (examples suggest
7237 * it might be used for the normal file contents).
7238 * Note that BackupRead() counts bytes, but the name is in
7239 * wide characters. */
7240 len = readcount / sizeof(WCHAR);
7241 streamname[len] = 0;
7242 if (len > 7 && wcsicmp(streamname + len - 6,
7243 L":$DATA") == 0)
7244 {
7245 streamname[len - 6] = 0;
7246 copy_substream(sh, &context, tow, streamname,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00007247 (long)sid.Size.u.LowPart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007248 }
7249 }
7250
7251 /* Advance to the next stream. We might try seeking too far,
7252 * but BackupSeek() doesn't skip over stream borders, thus
7253 * that's OK. */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00007254 (void)BackupSeek(sh, sid.Size.u.LowPart, sid.Size.u.HighPart,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007255 &lo, &hi, &context);
7256 }
7257
7258 /* Clear the context. */
7259 (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context);
7260
7261 CloseHandle(sh);
7262 }
7263 }
7264 vim_free(fromw);
7265 vim_free(tow);
7266}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007267
7268/*
7269 * Copy file attributes from file "from" to file "to".
7270 * For Windows NT and later we copy info streams.
7271 * Always returns zero, errors are ignored.
7272 */
7273 int
7274mch_copy_file_attribute(char_u *from, char_u *to)
7275{
Bram Moolenaar071d4272004-06-13 20:20:40 +00007276 /* File streams only work on Windows NT and later. */
7277 PlatformId();
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007278 copy_infostreams(from, to);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007279 return 0;
7280}
7281
7282#if defined(MYRESETSTKOFLW) || defined(PROTO)
7283/*
7284 * Recreate a destroyed stack guard page in win32.
7285 * Written by Benjamin Peterson.
7286 */
7287
7288/* These magic numbers are from the MS header files */
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007289# define MIN_STACK_WINNT 2
Bram Moolenaar071d4272004-06-13 20:20:40 +00007290
7291/*
7292 * This function does the same thing as _resetstkoflw(), which is only
7293 * available in DevStudio .net and later.
7294 * Returns 0 for failure, 1 for success.
7295 */
7296 int
7297myresetstkoflw(void)
7298{
7299 BYTE *pStackPtr;
7300 BYTE *pGuardPage;
7301 BYTE *pStackBase;
7302 BYTE *pLowestPossiblePage;
7303 MEMORY_BASIC_INFORMATION mbi;
7304 SYSTEM_INFO si;
7305 DWORD nPageSize;
7306 DWORD dummy;
7307
Bram Moolenaar071d4272004-06-13 20:20:40 +00007308 PlatformId();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007309
7310 /* We need to know the system page size. */
7311 GetSystemInfo(&si);
7312 nPageSize = si.dwPageSize;
7313
7314 /* ...and the current stack pointer */
7315 pStackPtr = (BYTE*)_alloca(1);
7316
7317 /* ...and the base of the stack. */
7318 if (VirtualQuery(pStackPtr, &mbi, sizeof mbi) == 0)
7319 return 0;
7320 pStackBase = (BYTE*)mbi.AllocationBase;
7321
7322 /* ...and the page thats min_stack_req pages away from stack base; this is
7323 * the lowest page we could use. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007324 pLowestPossiblePage = pStackBase + MIN_STACK_WINNT * nPageSize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007325
Bram Moolenaar071d4272004-06-13 20:20:40 +00007326 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007327 /* We want the first committed page in the stack Start at the stack
7328 * base and move forward through memory until we find a committed block.
7329 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007330 BYTE *pBlock = pStackBase;
7331
Bram Moolenaara466c992005-07-09 21:03:22 +00007332 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007333 {
7334 if (VirtualQuery(pBlock, &mbi, sizeof mbi) == 0)
7335 return 0;
7336
7337 pBlock += mbi.RegionSize;
7338
7339 if (mbi.State & MEM_COMMIT)
7340 break;
7341 }
7342
7343 /* mbi now describes the first committed block in the stack. */
7344 if (mbi.Protect & PAGE_GUARD)
7345 return 1;
7346
7347 /* decide where the guard page should start */
7348 if ((long_u)(mbi.BaseAddress) < (long_u)pLowestPossiblePage)
7349 pGuardPage = pLowestPossiblePage;
7350 else
7351 pGuardPage = (BYTE*)mbi.BaseAddress;
7352
7353 /* allocate the guard page */
7354 if (!VirtualAlloc(pGuardPage, nPageSize, MEM_COMMIT, PAGE_READWRITE))
7355 return 0;
7356
7357 /* apply the guard attribute to the page */
7358 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_READWRITE | PAGE_GUARD,
7359 &dummy))
7360 return 0;
7361 }
7362
7363 return 1;
7364}
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007365#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007366
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007367
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007368/*
7369 * The command line arguments in UCS2
7370 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00007371static int nArgsW = 0;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007372static LPWSTR *ArglistW = NULL;
7373static int global_argc = 0;
7374static char **global_argv;
7375
7376static int used_file_argc = 0; /* last argument in global_argv[] used
7377 for the argument list. */
7378static int *used_file_indexes = NULL; /* indexes in global_argv[] for
7379 command line arguments added to
7380 the argument list */
7381static int used_file_count = 0; /* nr of entries in used_file_indexes */
7382static int used_file_literal = FALSE; /* take file names literally */
7383static int used_file_full_path = FALSE; /* file name was full path */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007384static int used_file_diff_mode = FALSE; /* file name was with diff mode */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007385static int used_alist_count = 0;
7386
7387
7388/*
7389 * Get the command line arguments. Unicode version.
7390 * Returns argc. Zero when something fails.
7391 */
7392 int
7393get_cmd_argsW(char ***argvp)
7394{
7395 char **argv = NULL;
7396 int argc = 0;
7397 int i;
7398
Bram Moolenaar14993322014-09-09 12:25:33 +02007399 free_cmd_argsW();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007400 ArglistW = CommandLineToArgvW(GetCommandLineW(), &nArgsW);
7401 if (ArglistW != NULL)
7402 {
7403 argv = malloc((nArgsW + 1) * sizeof(char *));
7404 if (argv != NULL)
7405 {
7406 argc = nArgsW;
7407 argv[argc] = NULL;
7408 for (i = 0; i < argc; ++i)
7409 {
7410 int len;
7411
7412 /* Convert each Unicode argument to the current codepage. */
7413 WideCharToMultiByte_alloc(GetACP(), 0,
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007414 ArglistW[i], (int)wcslen(ArglistW[i]) + 1,
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007415 (LPSTR *)&argv[i], &len, 0, 0);
7416 if (argv[i] == NULL)
7417 {
7418 /* Out of memory, clear everything. */
7419 while (i > 0)
7420 free(argv[--i]);
7421 free(argv);
Bram Moolenaar73c61632013-12-07 14:48:10 +01007422 argv = NULL;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007423 argc = 0;
7424 }
7425 }
7426 }
7427 }
7428
7429 global_argc = argc;
7430 global_argv = argv;
7431 if (argc > 0)
Bram Moolenaar14993322014-09-09 12:25:33 +02007432 {
7433 if (used_file_indexes != NULL)
7434 free(used_file_indexes);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007435 used_file_indexes = malloc(argc * sizeof(int));
Bram Moolenaar14993322014-09-09 12:25:33 +02007436 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007437
7438 if (argvp != NULL)
7439 *argvp = argv;
7440 return argc;
7441}
7442
7443 void
7444free_cmd_argsW(void)
7445{
7446 if (ArglistW != NULL)
7447 {
7448 GlobalFree(ArglistW);
7449 ArglistW = NULL;
7450 }
7451}
7452
7453/*
7454 * Remember "name" is an argument that was added to the argument list.
7455 * This avoids that we have to re-parse the argument list when fix_arg_enc()
7456 * is called.
7457 */
7458 void
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007459used_file_arg(char *name, int literal, int full_path, int diff_mode)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007460{
7461 int i;
7462
7463 if (used_file_indexes == NULL)
7464 return;
7465 for (i = used_file_argc + 1; i < global_argc; ++i)
7466 if (STRCMP(global_argv[i], name) == 0)
7467 {
7468 used_file_argc = i;
7469 used_file_indexes[used_file_count++] = i;
7470 break;
7471 }
7472 used_file_literal = literal;
7473 used_file_full_path = full_path;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007474 used_file_diff_mode = diff_mode;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007475}
7476
7477/*
7478 * Remember the length of the argument list as it was. If it changes then we
7479 * leave it alone when 'encoding' is set.
7480 */
7481 void
7482set_alist_count(void)
7483{
7484 used_alist_count = GARGCOUNT;
7485}
7486
7487/*
7488 * Fix the encoding of the command line arguments. Invoked when 'encoding'
7489 * has been changed while starting up. Use the UCS-2 command line arguments
7490 * and convert them to 'encoding'.
7491 */
7492 void
7493fix_arg_enc(void)
7494{
7495 int i;
7496 int idx;
7497 char_u *str;
Bram Moolenaar86b68352004-12-27 21:59:20 +00007498 int *fnum_list;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007499
7500 /* Safety checks:
7501 * - if argument count differs between the wide and non-wide argument
7502 * list, something must be wrong.
7503 * - the file name arguments must have been located.
7504 * - the length of the argument list wasn't changed by the user.
7505 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00007506 if (global_argc != nArgsW
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007507 || ArglistW == NULL
7508 || used_file_indexes == NULL
7509 || used_file_count == 0
7510 || used_alist_count != GARGCOUNT)
7511 return;
7512
Bram Moolenaar86b68352004-12-27 21:59:20 +00007513 /* Remember the buffer numbers for the arguments. */
7514 fnum_list = (int *)alloc((int)sizeof(int) * GARGCOUNT);
7515 if (fnum_list == NULL)
7516 return; /* out of memory */
7517 for (i = 0; i < GARGCOUNT; ++i)
7518 fnum_list[i] = GARGLIST[i].ae_fnum;
7519
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007520 /* Clear the argument list. Make room for the new arguments. */
7521 alist_clear(&global_alist);
7522 if (ga_grow(&global_alist.al_ga, used_file_count) == FAIL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00007523 return; /* out of memory */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007524
7525 for (i = 0; i < used_file_count; ++i)
7526 {
7527 idx = used_file_indexes[i];
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007528 str = utf16_to_enc(ArglistW[idx], NULL);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007529 if (str != NULL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00007530 {
Bram Moolenaar39d21e32017-08-05 23:09:31 +02007531 int literal = used_file_literal;
7532
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007533# ifdef FEAT_DIFF
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007534 /* When using diff mode may need to concatenate file name to
7535 * directory name. Just like it's done in main(). */
7536 if (used_file_diff_mode && mch_isdir(str) && GARGCOUNT > 0
7537 && !mch_isdir(alist_name(&GARGLIST[0])))
7538 {
7539 char_u *r;
7540
7541 r = concat_fnames(str, gettail(alist_name(&GARGLIST[0])), TRUE);
7542 if (r != NULL)
7543 {
7544 vim_free(str);
7545 str = r;
7546 }
7547 }
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007548# endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00007549 /* Re-use the old buffer by renaming it. When not using literal
7550 * names it's done by alist_expand() below. */
7551 if (used_file_literal)
7552 buf_set_name(fnum_list[i], str);
7553
Bram Moolenaar39d21e32017-08-05 23:09:31 +02007554 /* Check backtick literal. backtick literal is already expanded in
7555 * main.c, so this part add str as literal. */
7556 if (literal == FALSE)
7557 {
Bram Moolenaar116a0f82017-08-07 21:17:57 +02007558 size_t len = STRLEN(str);
7559
Bram Moolenaar39d21e32017-08-05 23:09:31 +02007560 if (len > 2 && *str == '`' && *(str + len - 1) == '`')
7561 literal = TRUE;
7562 }
7563 alist_add(&global_alist, str, literal ? 2 : 0);
Bram Moolenaar86b68352004-12-27 21:59:20 +00007564 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007565 }
7566
7567 if (!used_file_literal)
7568 {
7569 /* Now expand wildcards in the arguments. */
7570 /* Temporarily add '(' and ')' to 'isfname'. These are valid
7571 * filename characters but are excluded from 'isfname' to make
Bram Moolenaar20586cb2018-03-08 22:03:14 +01007572 * "gf" work on a file name in parenthesis (e.g.: see vim.h).
7573 * Also, unset wildignore to not be influenced by this option.
7574 * The arguments specified in command-line should be kept even if
7575 * encoding options were changed. */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007576 do_cmdline_cmd((char_u *)":let SaVe_ISF = &isf|set isf+=(,)");
Bram Moolenaar20586cb2018-03-08 22:03:14 +01007577 do_cmdline_cmd((char_u *)":let SaVe_WIG = &wig|set wig=");
Bram Moolenaar86b68352004-12-27 21:59:20 +00007578 alist_expand(fnum_list, used_alist_count);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007579 do_cmdline_cmd((char_u *)":let &isf = SaVe_ISF|unlet SaVe_ISF");
Bram Moolenaar20586cb2018-03-08 22:03:14 +01007580 do_cmdline_cmd((char_u *)":let &wig = SaVe_WIG|unlet SaVe_WIG");
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007581 }
7582
7583 /* If wildcard expansion failed, we are editing the first file of the
7584 * arglist and there is no file name: Edit the first argument now. */
7585 if (curwin->w_arg_idx == 0 && curbuf->b_fname == NULL)
7586 {
7587 do_cmdline_cmd((char_u *)":rewind");
7588 if (GARGCOUNT == 1 && used_file_full_path)
Bram Moolenaarb7407d32018-02-03 17:36:27 +01007589 (void)vim_chdirfile(alist_name(&GARGLIST[0]), "drop");
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007590 }
Bram Moolenaar86b68352004-12-27 21:59:20 +00007591
7592 set_alist_count();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007593}
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007594
7595 int
7596mch_setenv(char *var, char *value, int x)
7597{
7598 char_u *envbuf;
7599
7600 envbuf = alloc((unsigned)(STRLEN(var) + STRLEN(value) + 2));
7601 if (envbuf == NULL)
7602 return -1;
7603
7604 sprintf((char *)envbuf, "%s=%s", var, value);
7605
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007606 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
7607 {
7608 WCHAR *p = enc_to_utf16(envbuf, NULL);
7609
7610 vim_free(envbuf);
7611 if (p == NULL)
7612 return -1;
7613 _wputenv(p);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007614#ifdef libintl_wputenv
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007615 libintl_wputenv(p);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007616#endif
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007617 /* Unlike Un*x systems, we can free the string for _wputenv(). */
7618 vim_free(p);
7619 }
7620 else
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007621 {
7622 _putenv((char *)envbuf);
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007623#ifdef libintl_putenv
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007624 libintl_putenv((char *)envbuf);
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007625#endif
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007626 /* Unlike Un*x systems, we can free the string for _putenv(). */
7627 vim_free(envbuf);
7628 }
7629
7630 return 0;
7631}
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007632
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007633/*
7634 * Support for 256 colors and 24-bit colors was added in Windows 10
7635 * version 1703 (Creators update).
7636 */
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007637#define VTP_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 15063)
7638
7639/*
7640 * Support for pseudo-console (ConPTY) was added in windows 10
7641 * version 1809 (October 2018 update).
7642 */
7643#define CONPTY_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 17763)
7644
7645 static void
7646vtp_flag_init(void)
7647{
7648 DWORD ver = get_build_number();
7649#ifndef FEAT_GUI_W32
7650 DWORD mode;
7651 HANDLE out;
7652
7653 out = GetStdHandle(STD_OUTPUT_HANDLE);
7654
7655 vtp_working = (ver >= VTP_FIRST_SUPPORT_BUILD) ? 1 : 0;
7656 GetConsoleMode(out, &mode);
7657 mode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
7658 if (SetConsoleMode(out, mode) == 0)
7659 vtp_working = 0;
7660#endif
7661
7662#ifdef FEAT_GUI_W32
7663 if (ver >= CONPTY_FIRST_SUPPORT_BUILD)
7664 vtp_working = 1;
7665#endif
7666
7667}
7668
7669#ifndef FEAT_GUI_W32
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007670
7671 static void
7672vtp_init(void)
7673{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007674 HMODULE hKerneldll;
7675 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi;
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007676# ifdef FEAT_TERMGUICOLORS
7677 COLORREF fg, bg;
7678# endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007679
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007680 /* Use functions supported from Vista */
7681 hKerneldll = GetModuleHandle("kernel32.dll");
7682 if (hKerneldll != NULL)
7683 {
7684 pGetConsoleScreenBufferInfoEx =
7685 (PfnGetConsoleScreenBufferInfoEx)GetProcAddress(
7686 hKerneldll, "GetConsoleScreenBufferInfoEx");
7687 pSetConsoleScreenBufferInfoEx =
7688 (PfnSetConsoleScreenBufferInfoEx)GetProcAddress(
7689 hKerneldll, "SetConsoleScreenBufferInfoEx");
7690 if (pGetConsoleScreenBufferInfoEx != NULL
7691 && pSetConsoleScreenBufferInfoEx != NULL)
7692 has_csbiex = TRUE;
7693 }
7694
7695 csbi.cbSize = sizeof(csbi);
7696 if (has_csbiex)
7697 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007698 save_console_bg_rgb = (guicolor_T)csbi.ColorTable[g_color_index_bg];
7699 save_console_fg_rgb = (guicolor_T)csbi.ColorTable[g_color_index_fg];
7700
7701# ifdef FEAT_TERMGUICOLORS
7702 bg = (COLORREF)csbi.ColorTable[g_color_index_bg];
7703 fg = (COLORREF)csbi.ColorTable[g_color_index_fg];
7704 bg = (GetRValue(bg) << 16) | (GetGValue(bg) << 8) | GetBValue(bg);
7705 fg = (GetRValue(fg) << 16) | (GetGValue(fg) << 8) | GetBValue(fg);
7706 default_console_color_bg = bg;
7707 default_console_color_fg = fg;
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007708# endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007709
7710 set_console_color_rgb();
7711}
7712
7713 static void
7714vtp_exit(void)
7715{
7716 reset_console_color_rgb();
7717}
7718
7719 static int
7720vtp_printf(
7721 char *format,
7722 ...)
7723{
7724 char_u buf[100];
7725 va_list list;
7726 DWORD result;
7727
7728 va_start(list, format);
7729 vim_vsnprintf((char *)buf, 100, (char *)format, list);
7730 va_end(list);
7731 WriteConsoleA(g_hConOut, buf, (DWORD)STRLEN(buf), &result, NULL);
7732 return (int)result;
7733}
7734
7735 static void
7736vtp_sgr_bulk(
7737 int arg)
7738{
7739 int args[1];
7740
7741 args[0] = arg;
7742 vtp_sgr_bulks(1, args);
7743}
7744
7745 static void
7746vtp_sgr_bulks(
7747 int argc,
7748 int *args
7749)
7750{
7751 /* 2('\033[') + 4('255.') * 16 + NUL */
7752 char_u buf[2 + (4 * 16) + 1];
7753 char_u *p;
7754 int i;
7755
7756 p = buf;
7757 *p++ = '\033';
7758 *p++ = '[';
7759
7760 for (i = 0; i < argc; ++i)
7761 {
7762 p += vim_snprintf((char *)p, 4, "%d", args[i] & 0xff);
7763 *p++ = ';';
7764 }
7765 p--;
7766 *p++ = 'm';
7767 *p = NUL;
7768 vtp_printf((char *)buf);
7769}
7770
Bram Moolenaar8a938af2018-05-01 17:30:41 +02007771# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007772 static int
7773ctermtoxterm(
7774 int cterm)
7775{
Bram Moolenaar9894e392018-05-05 14:29:06 +02007776 char_u r, g, b, idx;
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007777
7778 cterm_color2rgb(cterm, &r, &g, &b, &idx);
7779 return (((int)r << 16) | ((int)g << 8) | (int)b);
7780}
Bram Moolenaar8a938af2018-05-01 17:30:41 +02007781# endif
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007782
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007783 static void
7784set_console_color_rgb(void)
7785{
7786# ifdef FEAT_TERMGUICOLORS
7787 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi;
7788 int id;
7789 guicolor_T fg = INVALCOLOR;
7790 guicolor_T bg = INVALCOLOR;
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007791 int ctermfg;
7792 int ctermbg;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007793
7794 if (!USE_VTP)
7795 return;
7796
7797 id = syn_name2id((char_u *)"Normal");
7798 if (id > 0)
7799 syn_id2colors(id, &fg, &bg);
7800 if (fg == INVALCOLOR)
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007801 {
7802 ctermfg = -1;
7803 if (id > 0)
7804 syn_id2cterm_bg(id, &ctermfg, &ctermbg);
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007805 fg = ctermfg != -1 ? ctermtoxterm(ctermfg) : default_console_color_fg;
7806 cterm_normal_fg_gui_color = fg;
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007807 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007808 if (bg == INVALCOLOR)
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007809 {
7810 ctermbg = -1;
7811 if (id > 0)
7812 syn_id2cterm_bg(id, &ctermfg, &ctermbg);
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007813 bg = ctermbg != -1 ? ctermtoxterm(ctermbg) : default_console_color_bg;
7814 cterm_normal_bg_gui_color = bg;
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007815 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007816 fg = (GetRValue(fg) << 16) | (GetGValue(fg) << 8) | GetBValue(fg);
7817 bg = (GetRValue(bg) << 16) | (GetGValue(bg) << 8) | GetBValue(bg);
7818
7819 csbi.cbSize = sizeof(csbi);
7820 if (has_csbiex)
7821 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7822
7823 csbi.cbSize = sizeof(csbi);
7824 csbi.srWindow.Right += 1;
7825 csbi.srWindow.Bottom += 1;
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007826 csbi.ColorTable[g_color_index_bg] = (COLORREF)bg;
7827 csbi.ColorTable[g_color_index_fg] = (COLORREF)fg;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007828 if (has_csbiex)
7829 pSetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7830# endif
7831}
7832
7833 static void
7834reset_console_color_rgb(void)
7835{
7836# ifdef FEAT_TERMGUICOLORS
7837 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi;
7838
7839 csbi.cbSize = sizeof(csbi);
7840 if (has_csbiex)
7841 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7842
7843 csbi.cbSize = sizeof(csbi);
7844 csbi.srWindow.Right += 1;
7845 csbi.srWindow.Bottom += 1;
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007846 csbi.ColorTable[g_color_index_bg] = (COLORREF)save_console_bg_rgb;
7847 csbi.ColorTable[g_color_index_fg] = (COLORREF)save_console_fg_rgb;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007848 if (has_csbiex)
7849 pSetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7850# endif
7851}
7852
7853 void
7854control_console_color_rgb(void)
7855{
7856 if (USE_VTP)
7857 set_console_color_rgb();
7858 else
7859 reset_console_color_rgb();
7860}
7861
7862 int
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007863use_vtp(void)
7864{
7865 return USE_VTP;
7866}
7867
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007868 int
7869is_term_win32(void)
7870{
7871 return T_NAME != NULL && STRCMP(T_NAME, "win32") == 0;
7872}
7873
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007874#endif
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007875
7876 int
7877has_vtp_working(void)
7878{
7879 return vtp_working;
7880}