blob: 2c5670d9ddef1ce12bf3125731f71f484b7a58cf [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
Bram Moolenaar4f974752019-02-17 17:44:42 +010013 * the console version only, so there is a lot of "#ifndef FEAT_GUI_MSWIN".
Bram Moolenaar071d4272004-06-13 20:20:40 +000014 *
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
Bram Moolenaar0f873732019-12-05 20:28:46 +010033// cproto fails on missing include files
Bram Moolenaar82881492012-11-20 16:53:39 +010034#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
Bram Moolenaar4f974752019-02-17 17:44:42 +010048# if defined(FEAT_TITLE) && !defined(FEAT_GUI_MSWIN)
Bram Moolenaar82881492012-11-20 16:53:39 +010049# 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
Bram Moolenaar0f873732019-12-05 20:28:46 +010085// Record all output and all keyboard & mouse input
86// #define MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +000087
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
Bram Moolenaar912bc4a2019-12-01 18:58:11 +010097# 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;
Bram Moolenaar0f873732019-12-05 20:28:46 +0100146# define __stdcall // empty
Bram Moolenaar071d4272004-06-13 20:20:40 +0000147#endif
148
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200149#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar0f873732019-12-05 20:28:46 +0100150// Win32 Console handles for input and output
Bram Moolenaar071d4272004-06-13 20:20:40 +0000151static HANDLE g_hConIn = INVALID_HANDLE_VALUE;
152static HANDLE g_hConOut = INVALID_HANDLE_VALUE;
153
Bram Moolenaar0f873732019-12-05 20:28:46 +0100154// Win32 Screen buffer,coordinate,console I/O information
Bram Moolenaar071d4272004-06-13 20:20:40 +0000155static SMALL_RECT g_srScrollRegion;
Bram Moolenaar0f873732019-12-05 20:28:46 +0100156static COORD g_coord; // 0-based, but external coords are 1-based
Bram Moolenaar071d4272004-06-13 20:20:40 +0000157
Bram Moolenaar0f873732019-12-05 20:28:46 +0100158// The attribute of the screen when the editor was started
159static WORD g_attrDefault = 7; // lightgray text on black background
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160static WORD g_attrCurrent;
161
Bram Moolenaar0f873732019-12-05 20:28:46 +0100162static int g_fCBrkPressed = FALSE; // set by ctrl-break interrupt
163static int g_fCtrlCPressed = FALSE; // set when ctrl-C or ctrl-break detected
164static int g_fForceExit = FALSE; // set when forcefully exiting
Bram Moolenaar071d4272004-06-13 20:20:40 +0000165
Bram Moolenaar071d4272004-06-13 20:20:40 +0000166static void scroll(unsigned cLines);
167static void set_scroll_region(unsigned left, unsigned top,
168 unsigned right, unsigned bottom);
Bram Moolenaar6982f422019-02-16 16:48:01 +0100169static void set_scroll_region_tb(unsigned top, unsigned bottom);
170static void set_scroll_region_lr(unsigned left, unsigned right);
171static void insert_lines(unsigned cLines);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000172static void delete_lines(unsigned cLines);
173static void gotoxy(unsigned x, unsigned y);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000174static void standout(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000175static int s_cursor_visible = TRUE;
176static int did_create_conin = FALSE;
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200177#endif
178#ifdef FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +0000179static int s_dont_use_vimrun = TRUE;
180static int need_vimrun_warning = FALSE;
181static char *vimrun_path = "vimrun ";
182#endif
183
Bram Moolenaar12b559e2013-06-12 22:41:37 +0200184static int win32_getattrs(char_u *name);
185static int win32_setattrs(char_u *name, int attrs);
186static int win32_set_archive(char_u *name);
187
Bram Moolenaard9ef1b82019-02-13 19:23:10 +0100188static int conpty_working = 0;
Bram Moolenaar57da6982019-09-13 22:30:11 +0200189static int conpty_type = 0;
Bram Moolenaard9ef1b82019-02-13 19:23:10 +0100190static int conpty_stable = 0;
Bram Moolenaar7ed8f592020-04-28 20:44:42 +0200191static int conpty_fix_type = 0;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +0100192static void vtp_flag_init();
193
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200194#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar6902c0e2019-02-16 14:07:37 +0100195static int vtp_working = 0;
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100196static void vtp_init();
197static void vtp_exit();
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100198static void vtp_sgr_bulk(int arg);
199static void vtp_sgr_bulks(int argc, int *argv);
200
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200201static int wt_working = 0;
202static void wt_init();
203
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100204static guicolor_T save_console_bg_rgb;
205static guicolor_T save_console_fg_rgb;
Bram Moolenaardf543822020-01-30 11:53:59 +0100206static guicolor_T store_console_bg_rgb;
207static guicolor_T store_console_fg_rgb;
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100208
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +0200209static int g_color_index_bg = 0;
210static int g_color_index_fg = 7;
211
212# ifdef FEAT_TERMGUICOLORS
213static int default_console_color_bg = 0x000000; // black
214static int default_console_color_fg = 0xc0c0c0; // white
215# endif
216
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100217# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarc5cd8852018-05-01 15:47:38 +0200218# define USE_VTP (vtp_working && is_term_win32() && (p_tgc || (!p_tgc && t_colors >= 256)))
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200219# define USE_WT (wt_working)
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100220# else
221# define USE_VTP 0
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200222# define USE_WT 0
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100223# endif
224
225static void set_console_color_rgb(void);
226static void reset_console_color_rgb(void);
Bram Moolenaardf543822020-01-30 11:53:59 +0100227static void restore_console_color_rgb(void);
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100228#endif
229
Bram Moolenaar0f873732019-12-05 20:28:46 +0100230// This flag is newly created from Windows 10
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100231#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
232# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
233#endif
234
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200235#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar0f873732019-12-05 20:28:46 +0100236static int suppress_winsize = 1; // don't fiddle with console
Bram Moolenaar071d4272004-06-13 20:20:40 +0000237#endif
238
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200239static char_u *exe_path = NULL;
240
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100241static BOOL win8_or_later = FALSE;
242
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200243# if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__)
244# define UChar UnicodeChar
245# else
246# define UChar uChar.UnicodeChar
247# endif
248
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200249#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar0f873732019-12-05 20:28:46 +0100250// Dynamic loading for portability
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100251typedef struct _DYN_CONSOLE_SCREEN_BUFFER_INFOEX
252{
253 ULONG cbSize;
254 COORD dwSize;
255 COORD dwCursorPosition;
256 WORD wAttributes;
257 SMALL_RECT srWindow;
258 COORD dwMaximumWindowSize;
259 WORD wPopupAttributes;
260 BOOL bFullscreenSupported;
261 COLORREF ColorTable[16];
262} DYN_CONSOLE_SCREEN_BUFFER_INFOEX, *PDYN_CONSOLE_SCREEN_BUFFER_INFOEX;
263typedef BOOL (WINAPI *PfnGetConsoleScreenBufferInfoEx)(HANDLE, PDYN_CONSOLE_SCREEN_BUFFER_INFOEX);
264static PfnGetConsoleScreenBufferInfoEx pGetConsoleScreenBufferInfoEx;
265typedef BOOL (WINAPI *PfnSetConsoleScreenBufferInfoEx)(HANDLE, PDYN_CONSOLE_SCREEN_BUFFER_INFOEX);
266static PfnSetConsoleScreenBufferInfoEx pSetConsoleScreenBufferInfoEx;
267static BOOL has_csbiex = FALSE;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +0100268#endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100269
270/*
271 * Get version number including build number
272 */
273typedef BOOL (WINAPI *PfnRtlGetVersion)(LPOSVERSIONINFOW);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100274#define MAKE_VER(major, minor, build) \
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100275 (((major) << 24) | ((minor) << 16) | (build))
276
277 static DWORD
278get_build_number(void)
279{
280 OSVERSIONINFOW osver = {sizeof(OSVERSIONINFOW)};
281 HMODULE hNtdll;
282 PfnRtlGetVersion pRtlGetVersion;
283 DWORD ver = MAKE_VER(0, 0, 0);
284
285 hNtdll = GetModuleHandle("ntdll.dll");
286 if (hNtdll != NULL)
287 {
288 pRtlGetVersion =
289 (PfnRtlGetVersion)GetProcAddress(hNtdll, "RtlGetVersion");
290 pRtlGetVersion(&osver);
291 ver = MAKE_VER(min(osver.dwMajorVersion, 255),
292 min(osver.dwMinorVersion, 255),
293 min(osver.dwBuildNumber, 32767));
294 }
295 return ver;
296}
297
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200298#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200299 static BOOL
300is_ambiwidth_event(
301 INPUT_RECORD *ir)
302{
303 return ir->EventType == KEY_EVENT
304 && ir->Event.KeyEvent.bKeyDown
305 && ir->Event.KeyEvent.wRepeatCount == 1
306 && ir->Event.KeyEvent.wVirtualKeyCode == 0x12
307 && ir->Event.KeyEvent.wVirtualScanCode == 0x38
308 && ir->Event.KeyEvent.UChar == 0
309 && ir->Event.KeyEvent.dwControlKeyState == 2;
310}
311
312 static void
313make_ambiwidth_event(
314 INPUT_RECORD *down,
315 INPUT_RECORD *up)
316{
317 down->Event.KeyEvent.wVirtualKeyCode = 0;
318 down->Event.KeyEvent.wVirtualScanCode = 0;
319 down->Event.KeyEvent.UChar = up->Event.KeyEvent.UChar;
320 down->Event.KeyEvent.dwControlKeyState = 0;
321}
322
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100323/*
324 * Version of ReadConsoleInput() that works with IME.
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100325 * Works around problems on Windows 8.
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100326 */
327 static BOOL
328read_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100329 HANDLE hInput,
330 INPUT_RECORD *lpBuffer,
331 DWORD nLength,
332 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100333{
334 enum
335 {
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100336 IRSIZE = 10
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100337 };
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100338 static INPUT_RECORD s_irCache[IRSIZE];
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100339 static DWORD s_dwIndex = 0;
340 static DWORD s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100341 DWORD dwEvents;
Bram Moolenaardd415a62014-02-05 14:02:27 +0100342 int head;
343 int tail;
344 int i;
Bram Moolenaarbc970da2020-04-26 19:00:07 +0200345 static INPUT_RECORD s_irPseudo;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100346
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200347 if (nLength == -2)
348 return (s_dwMax > 0) ? TRUE : FALSE;
349
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100350 if (!win8_or_later)
351 {
352 if (nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200353 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
354 return ReadConsoleInputW(hInput, lpBuffer, 1, &dwEvents);
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100355 }
356
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100357 if (s_dwMax == 0)
358 {
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200359 if (!USE_WT && nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200360 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200361 GetNumberOfConsoleInputEvents(hInput, &dwEvents);
362 if (dwEvents == 0 && nLength == -1)
363 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
364 ReadConsoleInputW(hInput, s_irCache, IRSIZE, &dwEvents);
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100365 s_dwIndex = 0;
366 s_dwMax = dwEvents;
367 if (dwEvents == 0)
368 {
369 *lpEvents = 0;
370 return TRUE;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100371 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100372
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200373 for (i = s_dwIndex; i < (int)s_dwMax - 1; ++i)
374 if (is_ambiwidth_event(&s_irCache[i]))
375 make_ambiwidth_event(&s_irCache[i], &s_irCache[i + 1]);
376
Bram Moolenaardd415a62014-02-05 14:02:27 +0100377 if (s_dwMax > 1)
378 {
379 head = 0;
380 tail = s_dwMax - 1;
381 while (head != tail)
382 {
383 if (s_irCache[head].EventType == WINDOW_BUFFER_SIZE_EVENT
384 && s_irCache[head + 1].EventType
385 == WINDOW_BUFFER_SIZE_EVENT)
386 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100387 // Remove duplicate event to avoid flicker.
Bram Moolenaardd415a62014-02-05 14:02:27 +0100388 for (i = head; i < tail; ++i)
389 s_irCache[i] = s_irCache[i + 1];
390 --tail;
391 continue;
392 }
393 head++;
394 }
395 s_dwMax = tail + 1;
396 }
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100397 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100398
Bram Moolenaarbc970da2020-04-26 19:00:07 +0200399 if (s_irCache[s_dwIndex].EventType == KEY_EVENT)
400 {
401 if (s_irCache[s_dwIndex].Event.KeyEvent.wRepeatCount > 1)
402 {
403 s_irPseudo = s_irCache[s_dwIndex];
404 s_irPseudo.Event.KeyEvent.wRepeatCount = 1;
405 s_irCache[s_dwIndex].Event.KeyEvent.wRepeatCount--;
406 *lpBuffer = s_irPseudo;
407 *lpEvents = 1;
408 return TRUE;
409 }
410 }
411
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100412 *lpBuffer = s_irCache[s_dwIndex];
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200413 if (!(nLength == -1 || nLength == -2) && ++s_dwIndex >= s_dwMax)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100414 s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100415 *lpEvents = 1;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100416 return TRUE;
417}
418
419/*
420 * Version of PeekConsoleInput() that works with IME.
421 */
422 static BOOL
423peek_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100424 HANDLE hInput,
425 INPUT_RECORD *lpBuffer,
Bram Moolenaarbd67aac2019-09-21 23:09:04 +0200426 DWORD nLength UNUSED,
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100427 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100428{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100429 return read_console_input(hInput, lpBuffer, -1, lpEvents);
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100430}
431
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100432# ifdef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200433 static DWORD
434msg_wait_for_multiple_objects(
435 DWORD nCount,
436 LPHANDLE pHandles,
437 BOOL fWaitAll,
438 DWORD dwMilliseconds,
439 DWORD dwWakeMask)
440{
441 if (read_console_input(NULL, NULL, -2, NULL))
442 return WAIT_OBJECT_0;
443 return MsgWaitForMultipleObjects(nCount, pHandles, fWaitAll,
444 dwMilliseconds, dwWakeMask);
445}
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100446# endif
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200447
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100448# ifndef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200449 static DWORD
450wait_for_single_object(
451 HANDLE hHandle,
452 DWORD dwMilliseconds)
453{
454 if (read_console_input(NULL, NULL, -2, NULL))
455 return WAIT_OBJECT_0;
456 return WaitForSingleObject(hHandle, dwMilliseconds);
457}
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100458# endif
459#endif
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200460
Bram Moolenaar071d4272004-06-13 20:20:40 +0000461 static void
462get_exe_name(void)
463{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100464 // Maximum length of $PATH is more than MAXPATHL. 8191 is often mentioned
465 // as the maximum length that works (plus a NUL byte).
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100466#define MAX_ENV_PATH_LEN 8192
467 char temp[MAX_ENV_PATH_LEN];
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200468 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000469
470 if (exe_name == NULL)
471 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100472 // store the name of the executable, may be used for $VIM
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100473 GetModuleFileName(NULL, temp, MAX_ENV_PATH_LEN - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000474 if (*temp != NUL)
475 exe_name = FullName_save((char_u *)temp, FALSE);
476 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000477
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200478 if (exe_path == NULL && exe_name != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000479 {
Bram Moolenaar6b5ef062010-10-27 12:18:00 +0200480 exe_path = vim_strnsave(exe_name,
481 (int)(gettail_sep(exe_name) - exe_name));
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200482 if (exe_path != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000483 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100484 // Append our starting directory to $PATH, so that when doing
485 // "!xxd" it's found in our starting directory. Needed because
486 // SearchPath() also looks there.
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200487 p = mch_getenv("PATH");
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100488 if (p == NULL
489 || STRLEN(p) + STRLEN(exe_path) + 2 < MAX_ENV_PATH_LEN)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200490 {
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100491 if (p == NULL || *p == NUL)
492 temp[0] = NUL;
493 else
494 {
495 STRCPY(temp, p);
496 STRCAT(temp, ";");
497 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200498 STRCAT(temp, exe_path);
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100499 vim_setenv((char_u *)"PATH", (char_u *)temp);
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200500 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000501 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000502 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000503}
504
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200505/*
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100506 * Unescape characters in "p" that appear in "escaped".
507 */
508 static void
509unescape_shellxquote(char_u *p, char_u *escaped)
510{
Bram Moolenaar4336cdf2012-02-29 13:58:47 +0100511 int l = (int)STRLEN(p);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100512 int n;
513
514 while (*p != NUL)
515 {
516 if (*p == '^' && vim_strchr(escaped, p[1]) != NULL)
517 mch_memmove(p, p + 1, l--);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100518 n = (*mb_ptr2len)(p);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100519 p += n;
520 l -= n;
521 }
522}
523
524/*
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200525 * Load library "name".
526 */
527 HINSTANCE
528vimLoadLib(char *name)
529{
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200530 HINSTANCE dll = NULL;
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200531
Bram Moolenaar0f873732019-12-05 20:28:46 +0100532 // NOTE: Do not use mch_dirname() and mch_chdir() here, they may call
533 // vimLoadLib() recursively, which causes a stack overflow.
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200534 if (exe_path == NULL)
535 get_exe_name();
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200536 if (exe_path != NULL)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200537 {
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200538 WCHAR old_dirw[MAXPATHL];
539
540 if (GetCurrentDirectoryW(MAXPATHL, old_dirw) != 0)
541 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100542 // Change directory to where the executable is, both to make
543 // sure we find a .dll there and to avoid looking for a .dll
544 // in the current directory.
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100545 SetCurrentDirectory((LPCSTR)exe_path);
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200546 dll = LoadLibrary(name);
547 SetCurrentDirectoryW(old_dirw);
548 return dll;
549 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200550 }
551 return dll;
552}
553
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200554#if defined(VIMDLL) || defined(PROTO)
555/*
556 * Check if the current executable file is for the GUI subsystem.
557 */
558 int
559mch_is_gui_executable(void)
560{
561 PBYTE pImage = (PBYTE)GetModuleHandle(NULL);
562 PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)pImage;
563 PIMAGE_NT_HEADERS pPE;
564
565 if (pDOS->e_magic != IMAGE_DOS_SIGNATURE)
566 return FALSE;
567 pPE = (PIMAGE_NT_HEADERS)(pImage + pDOS->e_lfanew);
568 if (pPE->Signature != IMAGE_NT_SIGNATURE)
569 return FALSE;
570 if (pPE->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI)
571 return TRUE;
572 return FALSE;
573}
574#endif
575
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100576#if defined(DYNAMIC_ICONV) || defined(DYNAMIC_GETTEXT) || defined(PROTO)
577/*
578 * Get related information about 'funcname' which is imported by 'hInst'.
579 * If 'info' is 0, return the function address.
580 * If 'info' is 1, return the module name which the function is imported from.
581 */
582 static void *
583get_imported_func_info(HINSTANCE hInst, const char *funcname, int info)
584{
585 PBYTE pImage = (PBYTE)hInst;
586 PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)hInst;
587 PIMAGE_NT_HEADERS pPE;
588 PIMAGE_IMPORT_DESCRIPTOR pImpDesc;
Bram Moolenaar0f873732019-12-05 20:28:46 +0100589 PIMAGE_THUNK_DATA pIAT; // Import Address Table
590 PIMAGE_THUNK_DATA pINT; // Import Name Table
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100591 PIMAGE_IMPORT_BY_NAME pImpName;
592
593 if (pDOS->e_magic != IMAGE_DOS_SIGNATURE)
594 return NULL;
595 pPE = (PIMAGE_NT_HEADERS)(pImage + pDOS->e_lfanew);
596 if (pPE->Signature != IMAGE_NT_SIGNATURE)
597 return NULL;
598 pImpDesc = (PIMAGE_IMPORT_DESCRIPTOR)(pImage
599 + pPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
600 .VirtualAddress);
601 for (; pImpDesc->FirstThunk; ++pImpDesc)
602 {
603 if (!pImpDesc->OriginalFirstThunk)
604 continue;
605 pIAT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->FirstThunk);
606 pINT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->OriginalFirstThunk);
607 for (; pIAT->u1.Function; ++pIAT, ++pINT)
608 {
609 if (IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal))
610 continue;
611 pImpName = (PIMAGE_IMPORT_BY_NAME)(pImage
612 + (UINT_PTR)(pINT->u1.AddressOfData));
613 if (strcmp((char *)pImpName->Name, funcname) == 0)
614 {
615 switch (info)
616 {
617 case 0:
618 return (void *)pIAT->u1.Function;
619 case 1:
620 return (void *)(pImage + pImpDesc->Name);
621 default:
622 return NULL;
623 }
624 }
625 }
626 }
627 return NULL;
628}
629
630/*
631 * Get the module handle which 'funcname' in 'hInst' is imported from.
632 */
633 HINSTANCE
634find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname)
635{
636 char *modulename;
637
638 modulename = (char *)get_imported_func_info(hInst, funcname, 1);
639 if (modulename != NULL)
640 return GetModuleHandleA(modulename);
641 return NULL;
642}
643
644/*
645 * Get the address of 'funcname' which is imported by 'hInst' DLL.
646 */
647 void *
648get_dll_import_func(HINSTANCE hInst, const char *funcname)
649{
650 return get_imported_func_info(hInst, funcname, 0);
651}
652#endif
653
Bram Moolenaar071d4272004-06-13 20:20:40 +0000654#if defined(DYNAMIC_GETTEXT) || defined(PROTO)
655# ifndef GETTEXT_DLL
656# define GETTEXT_DLL "libintl.dll"
Bram Moolenaar7554c542018-10-06 15:03:15 +0200657# define GETTEXT_DLL_ALT1 "libintl-8.dll"
658# define GETTEXT_DLL_ALT2 "intl.dll"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000659# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +0100660// Dummy functions
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000661static char *null_libintl_gettext(const char *);
Bram Moolenaaree695f72016-08-03 22:08:45 +0200662static char *null_libintl_ngettext(const char *, const char *, unsigned long n);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000663static char *null_libintl_textdomain(const char *);
664static char *null_libintl_bindtextdomain(const char *, const char *);
665static char *null_libintl_bind_textdomain_codeset(const char *, const char *);
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100666static int null_libintl_wputenv(const wchar_t *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000667
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200668static HINSTANCE hLibintlDLL = NULL;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000669char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
Bram Moolenaaree695f72016-08-03 22:08:45 +0200670char *(*dyn_libintl_ngettext)(const char *, const char *, unsigned long n)
671 = null_libintl_ngettext;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000672char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
673char *(*dyn_libintl_bindtextdomain)(const char *, const char *)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000674 = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000675char *(*dyn_libintl_bind_textdomain_codeset)(const char *, const char *)
676 = null_libintl_bind_textdomain_codeset;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100677int (*dyn_libintl_wputenv)(const wchar_t *) = null_libintl_wputenv;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000678
679 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100680dyn_libintl_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000681{
682 int i;
683 static struct
684 {
685 char *name;
686 FARPROC *ptr;
687 } libintl_entry[] =
688 {
689 {"gettext", (FARPROC*)&dyn_libintl_gettext},
Bram Moolenaaree695f72016-08-03 22:08:45 +0200690 {"ngettext", (FARPROC*)&dyn_libintl_ngettext},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000691 {"textdomain", (FARPROC*)&dyn_libintl_textdomain},
692 {"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
693 {NULL, NULL}
694 };
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100695 HINSTANCE hmsvcrt;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000696
Bram Moolenaar7554c542018-10-06 15:03:15 +0200697 // No need to initialize twice.
698 if (hLibintlDLL != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000699 return 1;
Bram Moolenaar7554c542018-10-06 15:03:15 +0200700 // Load gettext library (libintl.dll and other names).
Bram Moolenaar923e43b2016-01-28 15:07:38 +0100701 hLibintlDLL = vimLoadLib(GETTEXT_DLL);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100702# ifdef GETTEXT_DLL_ALT1
Bram Moolenaar286eacd2016-01-16 18:05:50 +0100703 if (!hLibintlDLL)
Bram Moolenaar7554c542018-10-06 15:03:15 +0200704 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT1);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100705# endif
706# ifdef GETTEXT_DLL_ALT2
Bram Moolenaar7554c542018-10-06 15:03:15 +0200707 if (!hLibintlDLL)
708 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT2);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100709# endif
Bram Moolenaar938ee832016-01-24 15:36:03 +0100710 if (!hLibintlDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000711 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200712 if (p_verbose > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000713 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200714 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100715 semsg(_(e_loadlib), GETTEXT_DLL);
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200716 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200718 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000719 }
720 for (i = 0; libintl_entry[i].name != NULL
721 && libintl_entry[i].ptr != NULL; ++i)
722 {
723 if ((*libintl_entry[i].ptr = (FARPROC)GetProcAddress(hLibintlDLL,
724 libintl_entry[i].name)) == NULL)
725 {
726 dyn_libintl_end();
727 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000728 {
729 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100730 semsg(_(e_loadfunc), libintl_entry[i].name);
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000731 verbose_leave();
732 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733 return 0;
734 }
735 }
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000736
Bram Moolenaar0f873732019-12-05 20:28:46 +0100737 // The bind_textdomain_codeset() function is optional.
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000738 dyn_libintl_bind_textdomain_codeset = (void *)GetProcAddress(hLibintlDLL,
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000739 "bind_textdomain_codeset");
740 if (dyn_libintl_bind_textdomain_codeset == NULL)
741 dyn_libintl_bind_textdomain_codeset =
742 null_libintl_bind_textdomain_codeset;
743
Bram Moolenaar0f873732019-12-05 20:28:46 +0100744 // _wputenv() function for the libintl.dll is optional.
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100745 hmsvcrt = find_imported_module_by_funcname(hLibintlDLL, "getenv");
746 if (hmsvcrt != NULL)
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100747 dyn_libintl_wputenv = (void *)GetProcAddress(hmsvcrt, "_wputenv");
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100748 if (dyn_libintl_wputenv == NULL || dyn_libintl_wputenv == _wputenv)
749 dyn_libintl_wputenv = null_libintl_wputenv;
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100750
Bram Moolenaar071d4272004-06-13 20:20:40 +0000751 return 1;
752}
753
754 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100755dyn_libintl_end(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000756{
757 if (hLibintlDLL)
758 FreeLibrary(hLibintlDLL);
759 hLibintlDLL = NULL;
760 dyn_libintl_gettext = null_libintl_gettext;
Bram Moolenaaree695f72016-08-03 22:08:45 +0200761 dyn_libintl_ngettext = null_libintl_ngettext;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762 dyn_libintl_textdomain = null_libintl_textdomain;
763 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000764 dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100765 dyn_libintl_wputenv = null_libintl_wputenv;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000766}
767
768 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000769null_libintl_gettext(const char *msgid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000770{
771 return (char*)msgid;
772}
773
774 static char *
Bram Moolenaaree695f72016-08-03 22:08:45 +0200775null_libintl_ngettext(
776 const char *msgid,
777 const char *msgid_plural,
778 unsigned long n)
779{
Bram Moolenaarc90f2ae2016-08-04 22:00:15 +0200780 return (char *)(n == 1 ? msgid : msgid_plural);
Bram Moolenaaree695f72016-08-03 22:08:45 +0200781}
782
Bram Moolenaaree695f72016-08-03 22:08:45 +0200783 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100784null_libintl_bindtextdomain(
785 const char *domainname UNUSED,
786 const char *dirname UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000787{
788 return NULL;
789}
790
791 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100792null_libintl_bind_textdomain_codeset(
793 const char *domainname UNUSED,
794 const char *codeset UNUSED)
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000795{
796 return NULL;
797}
798
799 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100800null_libintl_textdomain(const char *domainname UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000801{
802 return NULL;
803}
804
Bram Moolenaare0ab9792017-08-02 23:18:25 +0200805 static int
Bram Moolenaar1266d672017-02-01 13:43:36 +0100806null_libintl_wputenv(const wchar_t *envstring UNUSED)
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100807{
808 return 0;
809}
810
Bram Moolenaar0f873732019-12-05 20:28:46 +0100811#endif // DYNAMIC_GETTEXT
Bram Moolenaar071d4272004-06-13 20:20:40 +0000812
Bram Moolenaar0f873732019-12-05 20:28:46 +0100813// This symbol is not defined in older versions of the SDK or Visual C++
Bram Moolenaar071d4272004-06-13 20:20:40 +0000814
815#ifndef VER_PLATFORM_WIN32_WINDOWS
816# define VER_PLATFORM_WIN32_WINDOWS 1
817#endif
818
Bram Moolenaar071d4272004-06-13 20:20:40 +0000819#ifdef HAVE_ACL
Bram Moolenaar82881492012-11-20 16:53:39 +0100820# ifndef PROTO
821# include <aclapi.h>
822# endif
Bram Moolenaar27515922013-06-29 15:36:26 +0200823# ifndef PROTECTED_DACL_SECURITY_INFORMATION
824# define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000L
825# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000826#endif
827
Bram Moolenaar27515922013-06-29 15:36:26 +0200828#ifdef HAVE_ACL
829/*
830 * Enables or disables the specified privilege.
831 */
832 static BOOL
833win32_enable_privilege(LPTSTR lpszPrivilege, BOOL bEnable)
834{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100835 BOOL bResult;
836 LUID luid;
837 HANDLE hToken;
838 TOKEN_PRIVILEGES tokenPrivileges;
Bram Moolenaar27515922013-06-29 15:36:26 +0200839
840 if (!OpenProcessToken(GetCurrentProcess(),
841 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
842 return FALSE;
843
844 if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
845 {
846 CloseHandle(hToken);
847 return FALSE;
848 }
849
Bram Moolenaar45500912014-07-09 20:51:07 +0200850 tokenPrivileges.PrivilegeCount = 1;
Bram Moolenaar27515922013-06-29 15:36:26 +0200851 tokenPrivileges.Privileges[0].Luid = luid;
852 tokenPrivileges.Privileges[0].Attributes = bEnable ?
853 SE_PRIVILEGE_ENABLED : 0;
854
855 bResult = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges,
856 sizeof(TOKEN_PRIVILEGES), NULL, NULL);
857
858 CloseHandle(hToken);
859
860 return bResult && GetLastError() == ERROR_SUCCESS;
861}
862#endif
863
Bram Moolenaar071d4272004-06-13 20:20:40 +0000864/*
Bram Moolenaar1e1d2e82020-05-18 20:17:02 +0200865 * Set "win8_or_later" and fill in "windowsVersion" if possible.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000866 */
867 void
868PlatformId(void)
869{
870 static int done = FALSE;
871
872 if (!done)
873 {
874 OSVERSIONINFO ovi;
875
876 ovi.dwOSVersionInfoSize = sizeof(ovi);
877 GetVersionEx(&ovi);
878
Bram Moolenaar1e1d2e82020-05-18 20:17:02 +0200879#ifdef FEAT_EVAL
Bram Moolenaar0c1e3742019-12-27 13:49:24 +0100880 vim_snprintf(windowsVersion, sizeof(windowsVersion), "%d.%d",
881 (int)ovi.dwMajorVersion, (int)ovi.dwMinorVersion);
Bram Moolenaar1e1d2e82020-05-18 20:17:02 +0200882#endif
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100883 if ((ovi.dwMajorVersion == 6 && ovi.dwMinorVersion >= 2)
884 || ovi.dwMajorVersion > 6)
885 win8_or_later = TRUE;
886
Bram Moolenaar071d4272004-06-13 20:20:40 +0000887#ifdef HAVE_ACL
Bram Moolenaar0f873732019-12-05 20:28:46 +0100888 // Enable privilege for getting or setting SACLs.
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200889 win32_enable_privilege(SE_SECURITY_NAME, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000890#endif
891 done = TRUE;
892 }
893}
894
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200895#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000896
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100897# define SHIFT (SHIFT_PRESSED)
898# define CTRL (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
899# define ALT (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
900# define ALT_GR (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000901
902
Bram Moolenaar0f873732019-12-05 20:28:46 +0100903// When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
904// We map function keys to their ANSI terminal equivalents, as produced
905// by ANSI.SYS, for compatibility with the MS-DOS version of Vim. Any
906// ANSI key with a value >= '\300' is nonstandard, but provided anyway
907// so that the user can have access to all SHIFT-, CTRL-, and ALT-
908// combinations of function/arrow/etc keys.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000909
Bram Moolenaard6f676d2005-06-01 21:51:55 +0000910static const struct
Bram Moolenaar071d4272004-06-13 20:20:40 +0000911{
912 WORD wVirtKey;
913 BOOL fAnsiKey;
914 int chAlone;
915 int chShift;
916 int chCtrl;
917 int chAlt;
918} VirtKeyMap[] =
919{
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200920// Key ANSI alone shift ctrl alt
Bram Moolenaar071d4272004-06-13 20:20:40 +0000921 { VK_ESCAPE,FALSE, ESC, ESC, ESC, ESC, },
922
923 { VK_F1, TRUE, ';', 'T', '^', 'h', },
924 { VK_F2, TRUE, '<', 'U', '_', 'i', },
925 { VK_F3, TRUE, '=', 'V', '`', 'j', },
926 { VK_F4, TRUE, '>', 'W', 'a', 'k', },
927 { VK_F5, TRUE, '?', 'X', 'b', 'l', },
928 { VK_F6, TRUE, '@', 'Y', 'c', 'm', },
929 { VK_F7, TRUE, 'A', 'Z', 'd', 'n', },
930 { VK_F8, TRUE, 'B', '[', 'e', 'o', },
931 { VK_F9, TRUE, 'C', '\\', 'f', 'p', },
932 { VK_F10, TRUE, 'D', ']', 'g', 'q', },
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200933 { VK_F11, TRUE, '\205', '\207', '\211', '\213', },
934 { VK_F12, TRUE, '\206', '\210', '\212', '\214', },
Bram Moolenaar071d4272004-06-13 20:20:40 +0000935
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200936 { VK_HOME, TRUE, 'G', '\302', 'w', '\303', },
937 { VK_UP, TRUE, 'H', '\304', '\305', '\306', },
938 { VK_PRIOR, TRUE, 'I', '\307', '\204', '\310', }, // PgUp
939 { VK_LEFT, TRUE, 'K', '\311', 's', '\312', },
940 { VK_RIGHT, TRUE, 'M', '\313', 't', '\314', },
941 { VK_END, TRUE, 'O', '\315', 'u', '\316', },
942 { VK_DOWN, TRUE, 'P', '\317', '\320', '\321', },
943 { VK_NEXT, TRUE, 'Q', '\322', 'v', '\323', }, // PgDn
944 { VK_INSERT,TRUE, 'R', '\324', '\325', '\326', },
945 { VK_DELETE,TRUE, 'S', '\327', '\330', '\331', },
Bram Moolenaarb70a47b2019-03-30 22:11:21 +0100946 { VK_BACK, TRUE, 'x', 'y', 'z', '{', }, // Backspace
Bram Moolenaar071d4272004-06-13 20:20:40 +0000947
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200948 { VK_SNAPSHOT,TRUE, 0, 0, 0, 'r', }, // PrtScrn
Bram Moolenaar071d4272004-06-13 20:20:40 +0000949
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100950# if 0
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200951 // Most people don't have F13-F20, but what the hell...
952 { VK_F13, TRUE, '\332', '\333', '\334', '\335', },
953 { VK_F14, TRUE, '\336', '\337', '\340', '\341', },
954 { VK_F15, TRUE, '\342', '\343', '\344', '\345', },
955 { VK_F16, TRUE, '\346', '\347', '\350', '\351', },
956 { VK_F17, TRUE, '\352', '\353', '\354', '\355', },
957 { VK_F18, TRUE, '\356', '\357', '\360', '\361', },
958 { VK_F19, TRUE, '\362', '\363', '\364', '\365', },
959 { VK_F20, TRUE, '\366', '\367', '\370', '\371', },
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100960# endif
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200961 { VK_ADD, TRUE, 'N', 'N', 'N', 'N', }, // keyp '+'
962 { VK_SUBTRACT, TRUE,'J', 'J', 'J', 'J', }, // keyp '-'
963 // { VK_DIVIDE, TRUE,'N', 'N', 'N', 'N', }, // keyp '/'
964 { VK_MULTIPLY, TRUE,'7', '7', '7', '7', }, // keyp '*'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000965
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200966 { VK_NUMPAD0,TRUE, '\332', '\333', '\334', '\335', },
967 { VK_NUMPAD1,TRUE, '\336', '\337', '\340', '\341', },
968 { VK_NUMPAD2,TRUE, '\342', '\343', '\344', '\345', },
969 { VK_NUMPAD3,TRUE, '\346', '\347', '\350', '\351', },
970 { VK_NUMPAD4,TRUE, '\352', '\353', '\354', '\355', },
971 { VK_NUMPAD5,TRUE, '\356', '\357', '\360', '\361', },
972 { VK_NUMPAD6,TRUE, '\362', '\363', '\364', '\365', },
973 { VK_NUMPAD7,TRUE, '\366', '\367', '\370', '\371', },
974 { VK_NUMPAD8,TRUE, '\372', '\373', '\374', '\375', },
975 // Sorry, out of number space! <negri>
Bram Moolenaarb70a47b2019-03-30 22:11:21 +0100976 { VK_NUMPAD9,TRUE, '\376', '\377', '|', '}', },
Bram Moolenaar071d4272004-06-13 20:20:40 +0000977};
978
979
Bram Moolenaar0f873732019-12-05 20:28:46 +0100980/*
981 * The return code indicates key code size.
982 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000983 static int
Bram Moolenaar071d4272004-06-13 20:20:40 +0000984win32_kbd_patch_key(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000985 KEY_EVENT_RECORD *pker)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000986{
987 UINT uMods = pker->dwControlKeyState;
988 static int s_iIsDead = 0;
989 static WORD awAnsiCode[2];
990 static BYTE abKeystate[256];
991
992
993 if (s_iIsDead == 2)
994 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200995 pker->UChar = (WCHAR) awAnsiCode[1];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000996 s_iIsDead = 0;
997 return 1;
998 }
999
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001000 if (pker->UChar != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001001 return 1;
1002
Bram Moolenaara80faa82020-04-12 19:37:17 +02001003 CLEAR_FIELD(abKeystate);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001004
Bram Moolenaar0f873732019-12-05 20:28:46 +01001005 // Clear any pending dead keys
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001006 ToUnicode(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001007
1008 if (uMods & SHIFT_PRESSED)
1009 abKeystate[VK_SHIFT] = 0x80;
1010 if (uMods & CAPSLOCK_ON)
1011 abKeystate[VK_CAPITAL] = 1;
1012
1013 if ((uMods & ALT_GR) == ALT_GR)
1014 {
1015 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
1016 abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
1017 }
1018
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001019 s_iIsDead = ToUnicode(pker->wVirtualKeyCode, pker->wVirtualScanCode,
1020 abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001021
1022 if (s_iIsDead > 0)
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001023 pker->UChar = (WCHAR) awAnsiCode[0];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001024
1025 return s_iIsDead;
1026}
1027
Bram Moolenaar071d4272004-06-13 20:20:40 +00001028static BOOL g_fJustGotFocus = FALSE;
1029
1030/*
1031 * Decode a KEY_EVENT into one or two keystrokes
1032 */
1033 static BOOL
1034decode_key_event(
1035 KEY_EVENT_RECORD *pker,
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001036 WCHAR *pch,
1037 WCHAR *pch2,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001038 int *pmodifiers,
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02001039 BOOL fDoPost UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040{
1041 int i;
1042 const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
1043
1044 *pch = *pch2 = NUL;
1045 g_fJustGotFocus = FALSE;
1046
Bram Moolenaar0f873732019-12-05 20:28:46 +01001047 // ignore key up events
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048 if (!pker->bKeyDown)
1049 return FALSE;
1050
Bram Moolenaar0f873732019-12-05 20:28:46 +01001051 // ignore some keystrokes
Bram Moolenaar071d4272004-06-13 20:20:40 +00001052 switch (pker->wVirtualKeyCode)
1053 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001054 // modifiers
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055 case VK_SHIFT:
1056 case VK_CONTROL:
Bram Moolenaar0f873732019-12-05 20:28:46 +01001057 case VK_MENU: // Alt key
Bram Moolenaar071d4272004-06-13 20:20:40 +00001058 return FALSE;
1059
1060 default:
1061 break;
1062 }
1063
Bram Moolenaar0f873732019-12-05 20:28:46 +01001064 // special cases
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001065 if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->UChar == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001066 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001067 // Ctrl-6 is Ctrl-^
Bram Moolenaar071d4272004-06-13 20:20:40 +00001068 if (pker->wVirtualKeyCode == '6')
1069 {
1070 *pch = Ctrl_HAT;
1071 return TRUE;
1072 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01001073 // Ctrl-2 is Ctrl-@
Bram Moolenaar071d4272004-06-13 20:20:40 +00001074 else if (pker->wVirtualKeyCode == '2')
1075 {
1076 *pch = NUL;
1077 return TRUE;
1078 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01001079 // Ctrl-- is Ctrl-_
Bram Moolenaar071d4272004-06-13 20:20:40 +00001080 else if (pker->wVirtualKeyCode == 0xBD)
1081 {
1082 *pch = Ctrl__;
1083 return TRUE;
1084 }
1085 }
1086
Bram Moolenaar0f873732019-12-05 20:28:46 +01001087 // Shift-TAB
Bram Moolenaar071d4272004-06-13 20:20:40 +00001088 if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED))
1089 {
1090 *pch = K_NUL;
1091 *pch2 = '\017';
1092 return TRUE;
1093 }
1094
1095 for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]); --i >= 0; )
1096 {
1097 if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
1098 {
1099 if (nModifs == 0)
1100 *pch = VirtKeyMap[i].chAlone;
1101 else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
1102 *pch = VirtKeyMap[i].chShift;
1103 else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
1104 *pch = VirtKeyMap[i].chCtrl;
1105 else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
1106 *pch = VirtKeyMap[i].chAlt;
1107
1108 if (*pch != 0)
1109 {
1110 if (VirtKeyMap[i].fAnsiKey)
1111 {
1112 *pch2 = *pch;
1113 *pch = K_NUL;
1114 }
1115
1116 return TRUE;
1117 }
1118 }
1119 }
1120
1121 i = win32_kbd_patch_key(pker);
1122
1123 if (i < 0)
1124 *pch = NUL;
1125 else
1126 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001127 *pch = (i > 0) ? pker->UChar : NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001128
1129 if (pmodifiers != NULL)
1130 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001131 // Pass on the ALT key as a modifier, but only when not combined
1132 // with CTRL (which is ALTGR).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001133 if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0)
1134 *pmodifiers |= MOD_MASK_ALT;
1135
Bram Moolenaar0f873732019-12-05 20:28:46 +01001136 // Pass on SHIFT only for special keys, because we don't know when
1137 // it's already included with the character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001138 if ((nModifs & SHIFT) != 0 && *pch <= 0x20)
1139 *pmodifiers |= MOD_MASK_SHIFT;
1140
Bram Moolenaar0f873732019-12-05 20:28:46 +01001141 // Pass on CTRL only for non-special keys, because we don't know
1142 // when it's already included with the character. And not when
1143 // combined with ALT (which is ALTGR).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001144 if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0
1145 && *pch >= 0x20 && *pch < 0x80)
1146 *pmodifiers |= MOD_MASK_CTRL;
1147 }
1148 }
1149
1150 return (*pch != NUL);
1151}
1152
Bram Moolenaar0f873732019-12-05 20:28:46 +01001153#endif // FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00001154
1155
Bram Moolenaar071d4272004-06-13 20:20:40 +00001156/*
1157 * For the GUI the mouse handling is in gui_w32.c.
1158 */
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001159#if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001160 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01001161mch_setmouse(int on UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001162{
1163}
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001164#else
Bram Moolenaar0f873732019-12-05 20:28:46 +01001165static int g_fMouseAvail = FALSE; // mouse present
1166static int g_fMouseActive = FALSE; // mouse enabled
1167static int g_nMouseClick = -1; // mouse status
1168static int g_xMouse; // mouse x coordinate
1169static int g_yMouse; // mouse y coordinate
Bram Moolenaar071d4272004-06-13 20:20:40 +00001170
1171/*
1172 * Enable or disable mouse input
1173 */
1174 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001175mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176{
1177 DWORD cmodein;
1178
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001179# ifdef VIMDLL
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001180 if (gui.in_use)
1181 return;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001182# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001183 if (!g_fMouseAvail)
1184 return;
1185
1186 g_fMouseActive = on;
1187 GetConsoleMode(g_hConIn, &cmodein);
1188
1189 if (g_fMouseActive)
1190 cmodein |= ENABLE_MOUSE_INPUT;
1191 else
1192 cmodein &= ~ENABLE_MOUSE_INPUT;
1193
1194 SetConsoleMode(g_hConIn, cmodein);
1195}
1196
Bram Moolenaar157d8132018-03-06 17:09:20 +01001197
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001198# if defined(FEAT_BEVAL_TERM) || defined(PROTO)
Bram Moolenaar157d8132018-03-06 17:09:20 +01001199/*
1200 * Called when 'balloonevalterm' changed.
1201 */
1202 void
1203mch_bevalterm_changed(void)
1204{
1205 mch_setmouse(g_fMouseActive);
1206}
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001207# endif
Bram Moolenaar157d8132018-03-06 17:09:20 +01001208
Bram Moolenaar071d4272004-06-13 20:20:40 +00001209/*
1210 * Decode a MOUSE_EVENT. If it's a valid event, return MOUSE_LEFT,
1211 * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
1212 * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
1213 * or a MOUSE_LEFT, _MIDDLE, or _RIGHT. We encode the button type,
1214 * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
1215 * and we return the mouse position in g_xMouse and g_yMouse.
1216 *
1217 * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
1218 * MOUSE_DRAGs and one MOUSE_RELEASE. MOUSE_RELEASE will be followed only
1219 * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
1220 *
1221 * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
1222 * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
1223 *
1224 * Windows will send us MOUSE_MOVED notifications whenever the mouse
1225 * moves, even if it stays within the same character cell. We ignore
1226 * all MOUSE_MOVED messages if the position hasn't really changed, and
1227 * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
1228 * we're only interested in MOUSE_DRAG).
1229 *
1230 * All of this is complicated by the code that fakes MOUSE_MIDDLE on
1231 * 2-button mouses by pressing the left & right buttons simultaneously.
1232 * In practice, it's almost impossible to click both at the same time,
1233 * so we need to delay a little. Also, we tend not to get MOUSE_RELEASE
1234 * in such cases, if the user is clicking quickly.
1235 */
1236 static BOOL
1237decode_mouse_event(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001238 MOUSE_EVENT_RECORD *pmer)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001239{
1240 static int s_nOldButton = -1;
1241 static int s_nOldMouseClick = -1;
1242 static int s_xOldMouse = -1;
1243 static int s_yOldMouse = -1;
1244 static linenr_T s_old_topline = 0;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001245# ifdef FEAT_DIFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00001246 static int s_old_topfill = 0;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001247# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 static int s_cClicks = 1;
1249 static BOOL s_fReleased = TRUE;
1250 static DWORD s_dwLastClickTime = 0;
1251 static BOOL s_fNextIsMiddle = FALSE;
1252
Bram Moolenaar0f873732019-12-05 20:28:46 +01001253 static DWORD cButtons = 0; // number of buttons supported
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254
1255 const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
1256 const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
1257 const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
1258 const DWORD LEFT_RIGHT = LEFT | RIGHT;
1259
1260 int nButton;
1261
1262 if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons))
1263 cButtons = 2;
1264
1265 if (!g_fMouseAvail || !g_fMouseActive)
1266 {
1267 g_nMouseClick = -1;
1268 return FALSE;
1269 }
1270
Bram Moolenaar0f873732019-12-05 20:28:46 +01001271 // get a spurious MOUSE_EVENT immediately after receiving focus; ignore
Bram Moolenaar071d4272004-06-13 20:20:40 +00001272 if (g_fJustGotFocus)
1273 {
1274 g_fJustGotFocus = FALSE;
1275 return FALSE;
1276 }
1277
Bram Moolenaar0f873732019-12-05 20:28:46 +01001278 // unprocessed mouse click?
Bram Moolenaar071d4272004-06-13 20:20:40 +00001279 if (g_nMouseClick != -1)
1280 return TRUE;
1281
1282 nButton = -1;
1283 g_xMouse = pmer->dwMousePosition.X;
1284 g_yMouse = pmer->dwMousePosition.Y;
1285
1286 if (pmer->dwEventFlags == MOUSE_MOVED)
1287 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001288 // Ignore MOUSE_MOVED events if (x, y) hasn't changed. (We get these
1289 // events even when the mouse moves only within a char cell.)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290 if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse)
1291 return FALSE;
1292 }
1293
Bram Moolenaar0f873732019-12-05 20:28:46 +01001294 // If no buttons are pressed...
Bram Moolenaar071d4272004-06-13 20:20:40 +00001295 if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0)
1296 {
Bram Moolenaar157d8132018-03-06 17:09:20 +01001297 nButton = MOUSE_RELEASE;
1298
Bram Moolenaar0f873732019-12-05 20:28:46 +01001299 // If the last thing returned was MOUSE_RELEASE, ignore this
Bram Moolenaar071d4272004-06-13 20:20:40 +00001300 if (s_fReleased)
Bram Moolenaar157d8132018-03-06 17:09:20 +01001301 {
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001302# ifdef FEAT_BEVAL_TERM
Bram Moolenaar0f873732019-12-05 20:28:46 +01001303 // do return mouse move events when we want them
Bram Moolenaar157d8132018-03-06 17:09:20 +01001304 if (p_bevalterm)
1305 nButton = MOUSE_DRAG;
1306 else
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001307# endif
Bram Moolenaar157d8132018-03-06 17:09:20 +01001308 return FALSE;
1309 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311 s_fReleased = TRUE;
1312 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01001313 else // one or more buttons pressed
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001315 // on a 2-button mouse, hold down left and right buttons
1316 // simultaneously to get MIDDLE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001317
1318 if (cButtons == 2 && s_nOldButton != MOUSE_DRAG)
1319 {
1320 DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
1321
Bram Moolenaar0f873732019-12-05 20:28:46 +01001322 // if either left or right button only is pressed, see if the
1323 // next mouse event has both of them pressed
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 if (dwLR == LEFT || dwLR == RIGHT)
1325 {
1326 for (;;)
1327 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001328 // wait a short time for next input event
Bram Moolenaar071d4272004-06-13 20:20:40 +00001329 if (WaitForSingleObject(g_hConIn, p_mouset / 3)
1330 != WAIT_OBJECT_0)
1331 break;
1332 else
1333 {
1334 DWORD cRecords = 0;
1335 INPUT_RECORD ir;
1336 MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
1337
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001338 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001339
1340 if (cRecords == 0 || ir.EventType != MOUSE_EVENT
1341 || !(pmer2->dwButtonState & LEFT_RIGHT))
1342 break;
1343 else
1344 {
1345 if (pmer2->dwEventFlags != MOUSE_MOVED)
1346 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001347 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001348
1349 return decode_mouse_event(pmer2);
1350 }
1351 else if (s_xOldMouse == pmer2->dwMousePosition.X &&
1352 s_yOldMouse == pmer2->dwMousePosition.Y)
1353 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001354 // throw away spurious mouse move
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001355 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001356
Bram Moolenaar0f873732019-12-05 20:28:46 +01001357 // are there any more mouse events in queue?
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001358 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001359
1360 if (cRecords==0 || ir.EventType != MOUSE_EVENT)
1361 break;
1362 }
1363 else
1364 break;
1365 }
1366 }
1367 }
1368 }
1369 }
1370
1371 if (s_fNextIsMiddle)
1372 {
1373 nButton = (pmer->dwEventFlags == MOUSE_MOVED)
1374 ? MOUSE_DRAG : MOUSE_MIDDLE;
1375 s_fNextIsMiddle = FALSE;
1376 }
1377 else if (cButtons == 2 &&
1378 ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
1379 {
1380 nButton = MOUSE_MIDDLE;
1381
1382 if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED)
1383 {
1384 s_fNextIsMiddle = TRUE;
1385 nButton = MOUSE_RELEASE;
1386 }
1387 }
1388 else if ((pmer->dwButtonState & LEFT) == LEFT)
1389 nButton = MOUSE_LEFT;
1390 else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
1391 nButton = MOUSE_MIDDLE;
1392 else if ((pmer->dwButtonState & RIGHT) == RIGHT)
1393 nButton = MOUSE_RIGHT;
1394
1395 if (! s_fReleased && ! s_fNextIsMiddle
1396 && nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG)
1397 return FALSE;
1398
1399 s_fReleased = s_fNextIsMiddle;
1400 }
1401
1402 if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK)
1403 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001404 // button pressed or released, without mouse moving
Bram Moolenaar071d4272004-06-13 20:20:40 +00001405 if (nButton != -1 && nButton != MOUSE_RELEASE)
1406 {
1407 DWORD dwCurrentTime = GetTickCount();
1408
1409 if (s_xOldMouse != g_xMouse
1410 || s_yOldMouse != g_yMouse
1411 || s_nOldButton != nButton
1412 || s_old_topline != curwin->w_topline
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001413# ifdef FEAT_DIFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00001414 || s_old_topfill != curwin->w_topfill
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001415# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001416 || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset)
1417 {
1418 s_cClicks = 1;
1419 }
1420 else if (++s_cClicks > 4)
1421 {
1422 s_cClicks = 1;
1423 }
1424
1425 s_dwLastClickTime = dwCurrentTime;
1426 }
1427 }
1428 else if (pmer->dwEventFlags == MOUSE_MOVED)
1429 {
1430 if (nButton != -1 && nButton != MOUSE_RELEASE)
1431 nButton = MOUSE_DRAG;
1432
1433 s_cClicks = 1;
1434 }
1435
1436 if (nButton == -1)
1437 return FALSE;
1438
1439 if (nButton != MOUSE_RELEASE)
1440 s_nOldButton = nButton;
1441
1442 g_nMouseClick = nButton;
1443
1444 if (pmer->dwControlKeyState & SHIFT_PRESSED)
1445 g_nMouseClick |= MOUSE_SHIFT;
1446 if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
1447 g_nMouseClick |= MOUSE_CTRL;
1448 if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
1449 g_nMouseClick |= MOUSE_ALT;
1450
1451 if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE)
1452 SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
1453
Bram Moolenaar0f873732019-12-05 20:28:46 +01001454 // only pass on interesting (i.e., different) mouse events
Bram Moolenaar071d4272004-06-13 20:20:40 +00001455 if (s_xOldMouse == g_xMouse
1456 && s_yOldMouse == g_yMouse
1457 && s_nOldMouseClick == g_nMouseClick)
1458 {
1459 g_nMouseClick = -1;
1460 return FALSE;
1461 }
1462
1463 s_xOldMouse = g_xMouse;
1464 s_yOldMouse = g_yMouse;
1465 s_old_topline = curwin->w_topline;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001466# ifdef FEAT_DIFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00001467 s_old_topfill = curwin->w_topfill;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001468# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001469 s_nOldMouseClick = g_nMouseClick;
1470
1471 return TRUE;
1472}
1473
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001474#endif // FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00001475
1476
1477#ifdef MCH_CURSOR_SHAPE
1478/*
1479 * Set the shape of the cursor.
1480 * 'thickness' can be from 1 (thin) to 99 (block)
1481 */
1482 static void
1483mch_set_cursor_shape(int thickness)
1484{
1485 CONSOLE_CURSOR_INFO ConsoleCursorInfo;
1486 ConsoleCursorInfo.dwSize = thickness;
1487 ConsoleCursorInfo.bVisible = s_cursor_visible;
1488
1489 SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo);
1490 if (s_cursor_visible)
1491 SetConsoleCursorPosition(g_hConOut, g_coord);
1492}
1493
1494 void
1495mch_update_cursor(void)
1496{
1497 int idx;
1498 int thickness;
1499
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001500# ifdef VIMDLL
1501 if (gui.in_use)
1502 return;
1503# endif
1504
Bram Moolenaar071d4272004-06-13 20:20:40 +00001505 /*
1506 * How the cursor is drawn depends on the current mode.
1507 */
1508 idx = get_shape_idx(FALSE);
1509
1510 if (shape_table[idx].shape == SHAPE_BLOCK)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001511 thickness = 99; // 100 doesn't work on W95
Bram Moolenaar071d4272004-06-13 20:20:40 +00001512 else
1513 thickness = shape_table[idx].percentage;
1514 mch_set_cursor_shape(thickness);
1515}
1516#endif
1517
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001518#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001519/*
1520 * Handle FOCUS_EVENT.
1521 */
1522 static void
1523handle_focus_event(INPUT_RECORD ir)
1524{
1525 g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
1526 ui_focus_change((int)g_fJustGotFocus);
1527}
1528
Bram Moolenaar78d21da2019-02-17 15:00:52 +01001529static void ResizeConBuf(HANDLE hConsole, COORD coordScreen);
1530
Bram Moolenaar071d4272004-06-13 20:20:40 +00001531/*
1532 * Wait until console input from keyboard or mouse is available,
1533 * or the time is up.
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001534 * When "ignore_input" is TRUE even wait when input is available.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001535 * Return TRUE if something is available FALSE if not.
1536 */
1537 static int
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001538WaitForChar(long msec, int ignore_input)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001539{
1540 DWORD dwNow = 0, dwEndTime = 0;
1541 INPUT_RECORD ir;
1542 DWORD cRecords;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001543 WCHAR ch, ch2;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001544# ifdef FEAT_TIMERS
Bram Moolenaar40b1b542016-04-20 20:18:23 +02001545 int tb_change_cnt = typebuf.tb_change_cnt;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001546# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001547
1548 if (msec > 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001549 // Wait until the specified time has elapsed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001550 dwEndTime = GetTickCount() + msec;
1551 else if (msec < 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001552 // Wait forever.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001553 dwEndTime = INFINITE;
1554
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01001555 // We need to loop until the end of the time period, because
1556 // we might get multiple unusable mouse events in that time.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001557 for (;;)
1558 {
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001559 // Only process messages when waiting.
1560 if (msec != 0)
1561 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001562# ifdef MESSAGE_QUEUE
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001563 parse_queued_messages();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001564# endif
1565# ifdef FEAT_MZSCHEME
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001566 mzvim_check_threads();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001567# endif
1568# ifdef FEAT_CLIENTSERVER
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001569 serverProcessPendingMessages();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001570# endif
Bram Moolenaared5a9d62018-09-06 13:14:43 +02001571 }
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001572
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001573 if (g_nMouseClick != -1
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001574# ifdef FEAT_CLIENTSERVER
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001575 || (!ignore_input && input_available())
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001576# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001577 )
1578 return TRUE;
1579
1580 if (msec > 0)
1581 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001582 // If the specified wait time has passed, return. Beware that
1583 // GetTickCount() may wrap around (overflow).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001584 dwNow = GetTickCount();
Bram Moolenaarb7512b72013-08-10 12:45:09 +02001585 if ((int)(dwNow - dwEndTime) >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001586 break;
1587 }
1588 if (msec != 0)
1589 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001590 DWORD dwWaitTime = dwEndTime - dwNow;
1591
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001592# ifdef FEAT_JOB_CHANNEL
Bram Moolenaar0f873732019-12-05 20:28:46 +01001593 // Check channel while waiting for input.
Bram Moolenaar9186a272016-02-23 19:34:01 +01001594 if (dwWaitTime > 100)
Bram Moolenaar8a8199e2016-11-26 15:13:33 +01001595 {
Bram Moolenaar9186a272016-02-23 19:34:01 +01001596 dwWaitTime = 100;
Bram Moolenaar0f873732019-12-05 20:28:46 +01001597 // If there is readahead then parse_queued_messages() timed out
1598 // and we should call it again soon.
Bram Moolenaar8a8199e2016-11-26 15:13:33 +01001599 if (channel_any_readahead())
1600 dwWaitTime = 10;
1601 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001602# endif
1603# ifdef FEAT_BEVAL_GUI
Bram Moolenaar59716a22017-03-01 20:32:44 +01001604 if (p_beval && dwWaitTime > 100)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001605 // The 'balloonexpr' may indirectly invoke a callback while
1606 // waiting for a character, need to check often.
Bram Moolenaar59716a22017-03-01 20:32:44 +01001607 dwWaitTime = 100;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001608# endif
1609# ifdef FEAT_MZSCHEME
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001610 if (mzthreads_allowed() && p_mzq > 0
1611 && (msec < 0 || (long)dwWaitTime > p_mzq))
Bram Moolenaar0f873732019-12-05 20:28:46 +01001612 dwWaitTime = p_mzq; // don't wait longer than 'mzquantum'
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001613# endif
1614# ifdef FEAT_TIMERS
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001615 // When waiting very briefly don't trigger timers.
1616 if (dwWaitTime > 10)
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001617 {
1618 long due_time;
1619
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001620 // Trigger timers and then get the time in msec until the next
1621 // one is due. Wait up to that time.
1622 due_time = check_due_timer();
1623 if (typebuf.tb_change_cnt != tb_change_cnt)
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001624 {
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001625 // timer may have used feedkeys().
1626 return FALSE;
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001627 }
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001628 if (due_time > 0 && dwWaitTime > (DWORD)due_time)
1629 dwWaitTime = due_time;
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001630 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001631# endif
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001632 if (
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001633# ifdef FEAT_CLIENTSERVER
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001634 // Wait for either an event on the console input or a
1635 // message in the client-server window.
1636 msg_wait_for_multiple_objects(1, &g_hConIn, FALSE,
1637 dwWaitTime, QS_SENDMESSAGE) != WAIT_OBJECT_0
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001638# else
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001639 wait_for_single_object(g_hConIn, dwWaitTime)
1640 != WAIT_OBJECT_0
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001641# endif
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01001642 )
1643 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001644 }
1645
1646 cRecords = 0;
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001647 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001648
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001649# ifdef FEAT_MBYTE_IME
Bram Moolenaar071d4272004-06-13 20:20:40 +00001650 if (State & CMDLINE && msg_row == Rows - 1)
1651 {
1652 CONSOLE_SCREEN_BUFFER_INFO csbi;
1653
1654 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1655 {
1656 if (csbi.dwCursorPosition.Y != msg_row)
1657 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001658 // The screen is now messed up, must redraw the
1659 // command line and later all the windows.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001660 redraw_all_later(CLEAR);
1661 cmdline_row -= (msg_row - csbi.dwCursorPosition.Y);
1662 redrawcmd();
1663 }
1664 }
1665 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001666# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001667
1668 if (cRecords > 0)
1669 {
1670 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
1671 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001672# ifdef FEAT_MBYTE_IME
Bram Moolenaar0f873732019-12-05 20:28:46 +01001673 // Windows IME sends two '\n's with only one 'ENTER'. First:
1674 // wVirtualKeyCode == 13. second: wVirtualKeyCode == 0
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001675 if (ir.Event.KeyEvent.UChar == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001676 && ir.Event.KeyEvent.wVirtualKeyCode == 13)
1677 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001678 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001679 continue;
1680 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001681# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001682 if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
1683 NULL, FALSE))
1684 return TRUE;
1685 }
1686
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001687 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001688
1689 if (ir.EventType == FOCUS_EVENT)
1690 handle_focus_event(ir);
1691 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
Bram Moolenaarc33ecb22018-02-11 16:40:45 +01001692 {
Bram Moolenaar78d21da2019-02-17 15:00:52 +01001693 COORD dwSize = ir.Event.WindowBufferSizeEvent.dwSize;
1694
Bram Moolenaar36698e32019-12-11 22:57:40 +01001695 // Only call shell_resized() when the size actually changed to
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001696 // avoid the screen is cleared.
Bram Moolenaar78d21da2019-02-17 15:00:52 +01001697 if (dwSize.X != Columns || dwSize.Y != Rows)
1698 {
1699 CONSOLE_SCREEN_BUFFER_INFO csbi;
1700 GetConsoleScreenBufferInfo(g_hConOut, &csbi);
Bram Moolenaar36698e32019-12-11 22:57:40 +01001701 dwSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
Bram Moolenaar78d21da2019-02-17 15:00:52 +01001702 dwSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
Bram Moolenaar36698e32019-12-11 22:57:40 +01001703 if (dwSize.X != Columns || dwSize.Y != Rows)
1704 {
1705 ResizeConBuf(g_hConOut, dwSize);
1706 shell_resized();
1707 }
Bram Moolenaar78d21da2019-02-17 15:00:52 +01001708 }
Bram Moolenaarc33ecb22018-02-11 16:40:45 +01001709 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001710 else if (ir.EventType == MOUSE_EVENT
1711 && decode_mouse_event(&ir.Event.MouseEvent))
1712 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001713 }
1714 else if (msec == 0)
1715 break;
1716 }
1717
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001718# ifdef FEAT_CLIENTSERVER
Bram Moolenaar0f873732019-12-05 20:28:46 +01001719 // Something might have been received while we were waiting.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720 if (input_available())
1721 return TRUE;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001722# endif
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001723
Bram Moolenaar071d4272004-06-13 20:20:40 +00001724 return FALSE;
1725}
1726
Bram Moolenaar071d4272004-06-13 20:20:40 +00001727/*
1728 * return non-zero if a character is available
1729 */
1730 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001731mch_char_avail(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001732{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001733# ifdef VIMDLL
1734 if (gui.in_use)
1735 return TRUE;
1736# endif
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001737 return WaitForChar(0L, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001738}
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001739
1740# if defined(FEAT_TERMINAL) || defined(PROTO)
1741/*
1742 * Check for any pending input or messages.
1743 */
1744 int
1745mch_check_messages(void)
1746{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001747# ifdef VIMDLL
1748 if (gui.in_use)
1749 return TRUE;
1750# endif
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001751 return WaitForChar(0L, TRUE);
1752}
1753# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001754
1755/*
1756 * Create the console input. Used when reading stdin doesn't work.
1757 */
1758 static void
1759create_conin(void)
1760{
1761 g_hConIn = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
1762 FILE_SHARE_READ|FILE_SHARE_WRITE,
1763 (LPSECURITY_ATTRIBUTES) NULL,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001764 OPEN_EXISTING, 0, (HANDLE)NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001765 did_create_conin = TRUE;
1766}
1767
1768/*
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001769 * Get a keystroke or a mouse event, use a blocking wait.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001770 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001771 static WCHAR
1772tgetch(int *pmodifiers, WCHAR *pch2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001773{
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001774 WCHAR ch;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775
1776 for (;;)
1777 {
1778 INPUT_RECORD ir;
1779 DWORD cRecords = 0;
1780
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001781# ifdef FEAT_CLIENTSERVER
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001782 (void)WaitForChar(-1L, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001783 if (input_available())
1784 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001785 if (g_nMouseClick != -1)
1786 return 0;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001787# endif
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001788 if (read_console_input(g_hConIn, &ir, 1, &cRecords) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001789 {
1790 if (did_create_conin)
1791 read_error_exit();
1792 create_conin();
1793 continue;
1794 }
1795
1796 if (ir.EventType == KEY_EVENT)
1797 {
1798 if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2,
1799 pmodifiers, TRUE))
1800 return ch;
1801 }
1802 else if (ir.EventType == FOCUS_EVENT)
1803 handle_focus_event(ir);
1804 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1805 shell_resized();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001806 else if (ir.EventType == MOUSE_EVENT)
1807 {
1808 if (decode_mouse_event(&ir.Event.MouseEvent))
1809 return 0;
1810 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 }
1812}
Bram Moolenaar0f873732019-12-05 20:28:46 +01001813#endif // !FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814
1815
1816/*
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02001817 * mch_inchar(): low-level input function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001818 * Get one or more characters from the keyboard or the mouse.
1819 * If time == 0, do not wait for characters.
1820 * If time == n, wait a short time for characters.
1821 * If time == -1, wait forever for characters.
1822 * Returns the number of characters read into buf.
1823 */
1824 int
1825mch_inchar(
Bram Moolenaar1266d672017-02-01 13:43:36 +01001826 char_u *buf UNUSED,
1827 int maxlen UNUSED,
1828 long time UNUSED,
1829 int tb_change_cnt UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001831#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001832
1833 int len;
1834 int c;
Bram Moolenaar8f027fe2020-03-04 23:21:35 +01001835# ifdef VIMDLL
1836// Extra space for maximum three CSIs. E.g. U+1B6DB -> 0xF0 0x9B 0x9B 0x9B.
1837# define TYPEAHEADSPACE 6
1838# else
1839# define TYPEAHEADSPACE 0
1840# endif
1841# define TYPEAHEADLEN (20 + TYPEAHEADSPACE)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001842 static char_u typeahead[TYPEAHEADLEN]; // previously typed bytes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001843 static int typeaheadlen = 0;
1844
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001845# ifdef VIMDLL
1846 if (gui.in_use)
1847 return 0;
1848# endif
1849
Bram Moolenaar0f873732019-12-05 20:28:46 +01001850 // First use any typeahead that was kept because "buf" was too small.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001851 if (typeaheadlen > 0)
1852 goto theend;
1853
Bram Moolenaar071d4272004-06-13 20:20:40 +00001854 if (time >= 0)
1855 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001856 if (!WaitForChar(time, FALSE)) // no character available
Bram Moolenaar071d4272004-06-13 20:20:40 +00001857 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001858 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01001859 else // time == -1, wait forever
Bram Moolenaar071d4272004-06-13 20:20:40 +00001860 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001861 mch_set_winsize_now(); // Allow winsize changes from now on
Bram Moolenaar071d4272004-06-13 20:20:40 +00001862
Bram Moolenaar3918c952005-03-15 22:34:55 +00001863 /*
1864 * If there is no character available within 2 seconds (default)
1865 * write the autoscript file to disk. Or cause the CursorHold event
1866 * to be triggered.
1867 */
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001868 if (!WaitForChar(p_ut, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001869 {
Bram Moolenaard35f9712005-12-18 22:02:33 +00001870 if (trigger_cursorhold() && maxlen >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001871 {
Bram Moolenaar3918c952005-03-15 22:34:55 +00001872 buf[0] = K_SPECIAL;
1873 buf[1] = KS_EXTRA;
1874 buf[2] = (int)KE_CURSORHOLD;
1875 return 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001876 }
Bram Moolenaar702517d2005-06-27 22:34:07 +00001877 before_blocking();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001878 }
1879 }
1880
1881 /*
1882 * Try to read as many characters as there are, until the buffer is full.
1883 */
1884
Bram Moolenaar0f873732019-12-05 20:28:46 +01001885 // we will get at least one key. Get more if they are available.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001886 g_fCBrkPressed = FALSE;
1887
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001888# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00001889 if (fdDump)
1890 fputc('[', fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001891# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001892
Bram Moolenaar0f873732019-12-05 20:28:46 +01001893 // Keep looping until there is something in the typeahead buffer and more
1894 // to get and still room in the buffer (up to two bytes for a char and
1895 // three bytes for a modifier).
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001896 while ((typeaheadlen == 0 || WaitForChar(0L, FALSE))
Bram Moolenaar8f027fe2020-03-04 23:21:35 +01001897 && typeaheadlen + 5 + TYPEAHEADSPACE <= TYPEAHEADLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001898 {
1899 if (typebuf_changed(tb_change_cnt))
1900 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001901 // "buf" may be invalid now if a client put something in the
1902 // typeahead buffer and "buf" is in the typeahead buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001903 typeaheadlen = 0;
1904 break;
1905 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001906 if (g_nMouseClick != -1)
1907 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001908# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00001909 if (fdDump)
1910 fprintf(fdDump, "{%02x @ %d, %d}",
1911 g_nMouseClick, g_xMouse, g_yMouse);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001912# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001913 typeahead[typeaheadlen++] = ESC + 128;
1914 typeahead[typeaheadlen++] = 'M';
1915 typeahead[typeaheadlen++] = g_nMouseClick;
1916 typeahead[typeaheadlen++] = g_xMouse + '!';
1917 typeahead[typeaheadlen++] = g_yMouse + '!';
1918 g_nMouseClick = -1;
1919 }
1920 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001921 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001922 WCHAR ch2 = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001923 int modifiers = 0;
1924
1925 c = tgetch(&modifiers, &ch2);
1926
1927 if (typebuf_changed(tb_change_cnt))
1928 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001929 // "buf" may be invalid now if a client put something in the
1930 // typeahead buffer and "buf" is in the typeahead buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001931 typeaheadlen = 0;
1932 break;
1933 }
1934
1935 if (c == Ctrl_C && ctrl_c_interrupts)
1936 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001937# if defined(FEAT_CLIENTSERVER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001938 trash_input_buf();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001939# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001940 got_int = TRUE;
1941 }
1942
Bram Moolenaar071d4272004-06-13 20:20:40 +00001943 if (g_nMouseClick == -1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001944 {
1945 int n = 1;
1946
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001947 if (ch2 == NUL)
1948 {
Bram Moolenaar8f027fe2020-03-04 23:21:35 +01001949 int i, j;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001950 char_u *p;
1951 WCHAR ch[2];
1952
1953 ch[0] = c;
Bram Moolenaar0f873732019-12-05 20:28:46 +01001954 if (c >= 0xD800 && c <= 0xDBFF) // High surrogate
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001955 {
1956 ch[1] = tgetch(&modifiers, &ch2);
1957 n++;
1958 }
1959 p = utf16_to_enc(ch, &n);
1960 if (p != NULL)
1961 {
Bram Moolenaar8f027fe2020-03-04 23:21:35 +01001962 for (i = 0, j = 0; i < n; i++)
1963 {
1964 typeahead[typeaheadlen + j++] = p[i];
1965# ifdef VIMDLL
1966 if (p[i] == CSI)
1967 {
1968 typeahead[typeaheadlen + j++] = KS_EXTRA;
1969 typeahead[typeaheadlen + j++] = KE_CSI;
1970 }
1971# endif
1972 }
1973 n = j;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001974 vim_free(p);
1975 }
1976 }
1977 else
Bram Moolenaar8f027fe2020-03-04 23:21:35 +01001978 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001979 typeahead[typeaheadlen] = c;
Bram Moolenaar8f027fe2020-03-04 23:21:35 +01001980# ifdef VIMDLL
1981 if (c == CSI)
1982 {
1983 typeahead[typeaheadlen + 1] = KS_EXTRA;
1984 typeahead[typeaheadlen + 2] = KE_CSI;
1985 n = 3;
1986 }
1987# endif
1988 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001989 if (ch2 != NUL)
1990 {
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +02001991 if (c == K_NUL)
Bram Moolenaarfeeb4d02017-12-05 15:14:46 +01001992 {
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +02001993 switch (ch2)
1994 {
1995 case (WCHAR)'\324': // SHIFT+Insert
1996 case (WCHAR)'\325': // CTRL+Insert
1997 case (WCHAR)'\327': // SHIFT+Delete
1998 case (WCHAR)'\330': // CTRL+Delete
1999 typeahead[typeaheadlen + n] = (char_u)ch2;
2000 n++;
2001 break;
2002
2003 default:
2004 typeahead[typeaheadlen + n] = 3;
2005 typeahead[typeaheadlen + n + 1] = (char_u)ch2;
2006 n += 2;
2007 break;
2008 }
Bram Moolenaarfeeb4d02017-12-05 15:14:46 +01002009 }
2010 else
2011 {
2012 typeahead[typeaheadlen + n] = 3;
2013 typeahead[typeaheadlen + n + 1] = (char_u)ch2;
2014 n += 2;
2015 }
Bram Moolenaar45500912014-07-09 20:51:07 +02002016 }
2017
Bram Moolenaar0f873732019-12-05 20:28:46 +01002018 // Use the ALT key to set the 8th bit of the character
2019 // when it's one byte, the 8th bit isn't set yet and not
2020 // using a double-byte encoding (would become a lead
2021 // byte).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002022 if ((modifiers & MOD_MASK_ALT)
2023 && n == 1
2024 && (typeahead[typeaheadlen] & 0x80) == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002025 && !enc_dbcs
Bram Moolenaar071d4272004-06-13 20:20:40 +00002026 )
2027 {
Bram Moolenaar85a3e5c2007-11-20 16:22:16 +00002028 n = (*mb_char2bytes)(typeahead[typeaheadlen] | 0x80,
2029 typeahead + typeaheadlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002030 modifiers &= ~MOD_MASK_ALT;
2031 }
2032
2033 if (modifiers != 0)
2034 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002035 // Prepend modifiers to the character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036 mch_memmove(typeahead + typeaheadlen + 3,
2037 typeahead + typeaheadlen, n);
2038 typeahead[typeaheadlen++] = K_SPECIAL;
2039 typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
2040 typeahead[typeaheadlen++] = modifiers;
2041 }
2042
2043 typeaheadlen += n;
2044
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002045# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00002046 if (fdDump)
2047 fputc(c, fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002048# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002049 }
2050 }
2051 }
2052
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002053# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00002054 if (fdDump)
2055 {
2056 fputs("]\n", fdDump);
2057 fflush(fdDump);
2058 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002059# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002060
Bram Moolenaar071d4272004-06-13 20:20:40 +00002061theend:
Bram Moolenaar0f873732019-12-05 20:28:46 +01002062 // Move typeahead to "buf", as much as fits.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002063 len = 0;
2064 while (len < maxlen && typeaheadlen > 0)
2065 {
2066 buf[len++] = typeahead[0];
2067 mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
2068 }
2069 return len;
2070
Bram Moolenaar0f873732019-12-05 20:28:46 +01002071#else // FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002072 return 0;
Bram Moolenaar0f873732019-12-05 20:28:46 +01002073#endif // FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002074}
2075
Bram Moolenaar82881492012-11-20 16:53:39 +01002076#ifndef PROTO
2077# ifndef __MINGW32__
Bram Moolenaar0f873732019-12-05 20:28:46 +01002078# include <shellapi.h> // required for FindExecutable()
Bram Moolenaar82881492012-11-20 16:53:39 +01002079# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002080#endif
2081
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002082/*
Bram Moolenaar43663192017-03-05 14:29:12 +01002083 * If "use_path" is TRUE: Return TRUE if "name" is in $PATH.
2084 * If "use_path" is FALSE: Return TRUE if "name" exists.
2085 * When returning TRUE and "path" is not NULL save the path and set "*path" to
2086 * the allocated memory.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00002087 * TODO: Should somehow check if it's really executable.
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002088 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002089 static int
Bram Moolenaar43663192017-03-05 14:29:12 +01002090executable_exists(char *name, char_u **path, int use_path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002091{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002092 WCHAR *p;
2093 WCHAR fnamew[_MAX_PATH];
2094 WCHAR *dumw;
2095 WCHAR *wcurpath, *wnewpath;
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02002096 long n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002097
Bram Moolenaar43663192017-03-05 14:29:12 +01002098 if (!use_path)
2099 {
Bram Moolenaarf7e894d2017-03-05 19:49:13 +01002100 if (mch_getperm((char_u *)name) != -1 && !mch_isdir((char_u *)name))
Bram Moolenaar43663192017-03-05 14:29:12 +01002101 {
2102 if (path != NULL)
Bram Moolenaar066029e2017-03-05 15:19:32 +01002103 {
Bram Moolenaarf7e894d2017-03-05 19:49:13 +01002104 if (mch_isFullName((char_u *)name))
Bram Moolenaar066029e2017-03-05 15:19:32 +01002105 *path = vim_strsave((char_u *)name);
2106 else
2107 *path = FullName_save((char_u *)name, FALSE);
2108 }
Bram Moolenaar43663192017-03-05 14:29:12 +01002109 return TRUE;
2110 }
2111 return FALSE;
2112 }
2113
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002114 p = enc_to_utf16((char_u *)name, NULL);
2115 if (p == NULL)
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02002116 return FALSE;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002117
2118 wcurpath = _wgetenv(L"PATH");
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002119 wnewpath = ALLOC_MULT(WCHAR, wcslen(wcurpath) + 3);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002120 if (wnewpath == NULL)
2121 return FALSE;
2122 wcscpy(wnewpath, L".;");
2123 wcscat(wnewpath, wcurpath);
2124 n = (long)SearchPathW(wnewpath, p, NULL, _MAX_PATH, fnamew, &dumw);
2125 vim_free(wnewpath);
2126 vim_free(p);
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02002127 if (n == 0)
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002128 return FALSE;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002129 if (GetFileAttributesW(fnamew) & FILE_ATTRIBUTE_DIRECTORY)
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002130 return FALSE;
Bram Moolenaarc7f02552014-04-01 21:00:59 +02002131 if (path != NULL)
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002132 *path = utf16_to_enc(fnamew, NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002133 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002134}
2135
Bram Moolenaar1eed5322019-02-26 17:03:54 +01002136#if (defined(__MINGW32__) && __MSVCRT_VERSION__ >= 0x800) || \
2137 (defined(_MSC_VER) && _MSC_VER >= 1400)
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002138/*
2139 * Bad parameter handler.
2140 *
2141 * Certain MS CRT functions will intentionally crash when passed invalid
2142 * parameters to highlight possible security holes. Setting this function as
2143 * the bad parameter handler will prevent the crash.
2144 *
2145 * In debug builds the parameters contain CRT information that might help track
2146 * down the source of a problem, but in non-debug builds the arguments are all
2147 * NULL/0. Debug builds will also produce assert dialogs from the CRT, it is
2148 * worth allowing these to make debugging of issues easier.
2149 */
2150 static void
2151bad_param_handler(const wchar_t *expression,
2152 const wchar_t *function,
2153 const wchar_t *file,
2154 unsigned int line,
2155 uintptr_t pReserved)
2156{
2157}
2158
2159# define SET_INVALID_PARAM_HANDLER \
2160 ((void)_set_invalid_parameter_handler(bad_param_handler))
2161#else
2162# define SET_INVALID_PARAM_HANDLER
2163#endif
2164
Bram Moolenaar4f974752019-02-17 17:44:42 +01002165#ifdef FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002166
2167/*
2168 * GUI version of mch_init().
2169 */
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002170 static void
2171mch_init_g(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002172{
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002173# ifndef __MINGW32__
Bram Moolenaar071d4272004-06-13 20:20:40 +00002174 extern int _fmode;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002175# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002176
Bram Moolenaar0f873732019-12-05 20:28:46 +01002177 // Silently handle invalid parameters to CRT functions
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002178 SET_INVALID_PARAM_HANDLER;
2179
Bram Moolenaar0f873732019-12-05 20:28:46 +01002180 // Let critical errors result in a failure, not in a dialog box. Required
2181 // for the timestamp test to work on removed floppies.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002182 SetErrorMode(SEM_FAILCRITICALERRORS);
2183
Bram Moolenaar0f873732019-12-05 20:28:46 +01002184 _fmode = O_BINARY; // we do our own CR-LF translation
Bram Moolenaar071d4272004-06-13 20:20:40 +00002185
Bram Moolenaar0f873732019-12-05 20:28:46 +01002186 // Specify window size. Is there a place to get the default from?
Bram Moolenaar071d4272004-06-13 20:20:40 +00002187 Rows = 25;
2188 Columns = 80;
2189
Bram Moolenaar0f873732019-12-05 20:28:46 +01002190 // Look for 'vimrun'
Bram Moolenaar071d4272004-06-13 20:20:40 +00002191 {
2192 char_u vimrun_location[_MAX_PATH + 4];
2193
Bram Moolenaar0f873732019-12-05 20:28:46 +01002194 // First try in same directory as gvim.exe
Bram Moolenaar071d4272004-06-13 20:20:40 +00002195 STRCPY(vimrun_location, exe_name);
2196 STRCPY(gettail(vimrun_location), "vimrun.exe");
2197 if (mch_getperm(vimrun_location) >= 0)
2198 {
2199 if (*skiptowhite(vimrun_location) != NUL)
2200 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002201 // Enclose path with white space in double quotes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002202 mch_memmove(vimrun_location + 1, vimrun_location,
2203 STRLEN(vimrun_location) + 1);
2204 *vimrun_location = '"';
2205 STRCPY(gettail(vimrun_location), "vimrun\" ");
2206 }
2207 else
2208 STRCPY(gettail(vimrun_location), "vimrun ");
2209
2210 vimrun_path = (char *)vim_strsave(vimrun_location);
2211 s_dont_use_vimrun = FALSE;
2212 }
Bram Moolenaar43663192017-03-05 14:29:12 +01002213 else if (executable_exists("vimrun.exe", NULL, TRUE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002214 s_dont_use_vimrun = FALSE;
2215
Bram Moolenaar0f873732019-12-05 20:28:46 +01002216 // Don't give the warning for a missing vimrun.exe right now, but only
2217 // when vimrun was supposed to be used. Don't bother people that do
2218 // not need vimrun.exe.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002219 if (s_dont_use_vimrun)
2220 need_vimrun_warning = TRUE;
2221 }
2222
2223 /*
2224 * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
2225 * Otherwise the default "findstr /n" is used.
2226 */
Bram Moolenaar43663192017-03-05 14:29:12 +01002227 if (!executable_exists("findstr.exe", NULL, TRUE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002228 set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
2229
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002230# ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002231 win_clip_init();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002232# endif
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01002233
2234 vtp_flag_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002235}
2236
2237
Bram Moolenaar0f873732019-12-05 20:28:46 +01002238#endif // FEAT_GUI_MSWIN
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002239
2240#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002241
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002242# define SRWIDTH(sr) ((sr).Right - (sr).Left + 1)
2243# define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002244
2245/*
2246 * ClearConsoleBuffer()
2247 * Description:
2248 * Clears the entire contents of the console screen buffer, using the
2249 * specified attribute.
2250 * Returns:
2251 * TRUE on success
2252 */
2253 static BOOL
2254ClearConsoleBuffer(WORD wAttribute)
2255{
2256 CONSOLE_SCREEN_BUFFER_INFO csbi;
2257 COORD coord;
2258 DWORD NumCells, dummy;
2259
2260 if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2261 return FALSE;
2262
2263 NumCells = csbi.dwSize.X * csbi.dwSize.Y;
2264 coord.X = 0;
2265 coord.Y = 0;
2266 if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells,
2267 coord, &dummy))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002268 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002269 if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells,
2270 coord, &dummy))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002272
2273 return TRUE;
2274}
2275
2276/*
2277 * FitConsoleWindow()
2278 * Description:
2279 * Checks if the console window will fit within given buffer dimensions.
2280 * Also, if requested, will shrink the window to fit.
2281 * Returns:
2282 * TRUE on success
2283 */
2284 static BOOL
2285FitConsoleWindow(
2286 COORD dwBufferSize,
2287 BOOL WantAdjust)
2288{
2289 CONSOLE_SCREEN_BUFFER_INFO csbi;
2290 COORD dwWindowSize;
2291 BOOL NeedAdjust = FALSE;
2292
2293 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2294 {
2295 /*
2296 * A buffer resize will fail if the current console window does
2297 * not lie completely within that buffer. To avoid this, we might
2298 * have to move and possibly shrink the window.
2299 */
2300 if (csbi.srWindow.Right >= dwBufferSize.X)
2301 {
2302 dwWindowSize.X = SRWIDTH(csbi.srWindow);
2303 if (dwWindowSize.X > dwBufferSize.X)
2304 dwWindowSize.X = dwBufferSize.X;
2305 csbi.srWindow.Right = dwBufferSize.X - 1;
2306 csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X;
2307 NeedAdjust = TRUE;
2308 }
2309 if (csbi.srWindow.Bottom >= dwBufferSize.Y)
2310 {
2311 dwWindowSize.Y = SRHEIGHT(csbi.srWindow);
2312 if (dwWindowSize.Y > dwBufferSize.Y)
2313 dwWindowSize.Y = dwBufferSize.Y;
2314 csbi.srWindow.Bottom = dwBufferSize.Y - 1;
2315 csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y;
2316 NeedAdjust = TRUE;
2317 }
2318 if (NeedAdjust && WantAdjust)
2319 {
2320 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow))
2321 return FALSE;
2322 }
2323 return TRUE;
2324 }
2325
2326 return FALSE;
2327}
2328
2329typedef struct ConsoleBufferStruct
2330{
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002331 BOOL IsValid;
2332 CONSOLE_SCREEN_BUFFER_INFO Info;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002333 PCHAR_INFO Buffer;
2334 COORD BufferSize;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002335 PSMALL_RECT Regions;
2336 int NumRegions;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002337} ConsoleBuffer;
2338
2339/*
2340 * SaveConsoleBuffer()
2341 * Description:
2342 * Saves important information about the console buffer, including the
2343 * actual buffer contents. The saved information is suitable for later
2344 * restoration by RestoreConsoleBuffer().
2345 * Returns:
2346 * TRUE if all information was saved; FALSE otherwise
2347 * If FALSE, still sets cb->IsValid if buffer characteristics were saved.
2348 */
2349 static BOOL
2350SaveConsoleBuffer(
2351 ConsoleBuffer *cb)
2352{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002353 DWORD NumCells;
2354 COORD BufferCoord;
2355 SMALL_RECT ReadRegion;
2356 WORD Y, Y_incr;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002357 int i, numregions;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002358
Bram Moolenaar071d4272004-06-13 20:20:40 +00002359 if (cb == NULL)
2360 return FALSE;
2361
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002362 if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002363 {
2364 cb->IsValid = FALSE;
2365 return FALSE;
2366 }
2367 cb->IsValid = TRUE;
2368
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002369 /*
2370 * Allocate a buffer large enough to hold the entire console screen
2371 * buffer. If this ConsoleBuffer structure has already been initialized
2372 * with a buffer of the correct size, then just use that one.
2373 */
2374 if (!cb->IsValid || cb->Buffer == NULL ||
2375 cb->BufferSize.X != cb->Info.dwSize.X ||
2376 cb->BufferSize.Y != cb->Info.dwSize.Y)
2377 {
2378 cb->BufferSize.X = cb->Info.dwSize.X;
2379 cb->BufferSize.Y = cb->Info.dwSize.Y;
2380 NumCells = cb->BufferSize.X * cb->BufferSize.Y;
2381 vim_free(cb->Buffer);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002382 cb->Buffer = ALLOC_MULT(CHAR_INFO, NumCells);
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002383 if (cb->Buffer == NULL)
2384 return FALSE;
2385 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002386
2387 /*
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002388 * We will now copy the console screen buffer into our buffer.
2389 * ReadConsoleOutput() seems to be limited as far as how much you
2390 * can read at a time. Empirically, this number seems to be about
2391 * 12000 cells (rows * columns). Start at position (0, 0) and copy
2392 * in chunks until it is all copied. The chunks will all have the
2393 * same horizontal characteristics, so initialize them now. The
2394 * height of each chunk will be (12000 / width).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002395 */
Bram Moolenaar61594242015-09-01 20:23:37 +02002396 BufferCoord.X = 0;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002397 ReadRegion.Left = 0;
2398 ReadRegion.Right = cb->Info.dwSize.X - 1;
2399 Y_incr = 12000 / cb->Info.dwSize.X;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002400
2401 numregions = (cb->Info.dwSize.Y + Y_incr - 1) / Y_incr;
2402 if (cb->Regions == NULL || numregions != cb->NumRegions)
2403 {
2404 cb->NumRegions = numregions;
2405 vim_free(cb->Regions);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002406 cb->Regions = ALLOC_MULT(SMALL_RECT, cb->NumRegions);
Bram Moolenaar444fda22017-08-11 20:37:00 +02002407 if (cb->Regions == NULL)
2408 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01002409 VIM_CLEAR(cb->Buffer);
Bram Moolenaar444fda22017-08-11 20:37:00 +02002410 return FALSE;
2411 }
2412 }
2413
2414 for (i = 0, Y = 0; i < cb->NumRegions; i++, Y += Y_incr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002415 {
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002416 /*
2417 * Read into position (0, Y) in our buffer.
2418 */
2419 BufferCoord.Y = Y;
2420 /*
2421 * Read the region whose top left corner is (0, Y) and whose bottom
2422 * right corner is (width - 1, Y + Y_incr - 1). This should define
2423 * a region of size width by Y_incr. Don't worry if this region is
2424 * too large for the remaining buffer; it will be cropped.
2425 */
2426 ReadRegion.Top = Y;
2427 ReadRegion.Bottom = Y + Y_incr - 1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01002428 if (!ReadConsoleOutputW(g_hConOut, // output handle
2429 cb->Buffer, // our buffer
2430 cb->BufferSize, // dimensions of our buffer
2431 BufferCoord, // offset in our buffer
2432 &ReadRegion)) // region to save
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002433 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01002434 VIM_CLEAR(cb->Buffer);
2435 VIM_CLEAR(cb->Regions);
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002436 return FALSE;
2437 }
Bram Moolenaar444fda22017-08-11 20:37:00 +02002438 cb->Regions[i] = ReadRegion;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002439 }
2440
2441 return TRUE;
2442}
2443
2444/*
2445 * RestoreConsoleBuffer()
2446 * Description:
2447 * Restores important information about the console buffer, including the
2448 * actual buffer contents, if desired. The information to restore is in
2449 * the same format used by SaveConsoleBuffer().
2450 * Returns:
2451 * TRUE on success
2452 */
2453 static BOOL
2454RestoreConsoleBuffer(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002455 ConsoleBuffer *cb,
2456 BOOL RestoreScreen)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002458 COORD BufferCoord;
2459 SMALL_RECT WriteRegion;
Bram Moolenaar444fda22017-08-11 20:37:00 +02002460 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002461
2462 if (cb == NULL || !cb->IsValid)
2463 return FALSE;
2464
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002465 /*
2466 * Before restoring the buffer contents, clear the current buffer, and
2467 * restore the cursor position and window information. Doing this now
2468 * prevents old buffer contents from "flashing" onto the screen.
2469 */
2470 if (RestoreScreen)
2471 ClearConsoleBuffer(cb->Info.wAttributes);
2472
2473 FitConsoleWindow(cb->Info.dwSize, TRUE);
2474 if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
2475 return FALSE;
2476 if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
2477 return FALSE;
2478
2479 if (!RestoreScreen)
2480 {
2481 /*
2482 * No need to restore the screen buffer contents, so we're done.
2483 */
2484 return TRUE;
2485 }
2486
2487 if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
2488 return FALSE;
2489 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
2490 return FALSE;
2491
2492 /*
2493 * Restore the screen buffer contents.
2494 */
2495 if (cb->Buffer != NULL)
2496 {
Bram Moolenaar444fda22017-08-11 20:37:00 +02002497 for (i = 0; i < cb->NumRegions; i++)
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002498 {
Bram Moolenaar444fda22017-08-11 20:37:00 +02002499 BufferCoord.X = cb->Regions[i].Left;
2500 BufferCoord.Y = cb->Regions[i].Top;
2501 WriteRegion = cb->Regions[i];
Bram Moolenaar0f873732019-12-05 20:28:46 +01002502 if (!WriteConsoleOutputW(g_hConOut, // output handle
2503 cb->Buffer, // our buffer
2504 cb->BufferSize, // dimensions of our buffer
2505 BufferCoord, // offset in our buffer
2506 &WriteRegion)) // region to restore
Bram Moolenaar444fda22017-08-11 20:37:00 +02002507 return FALSE;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002508 }
2509 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002510
2511 return TRUE;
2512}
2513
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002514# define FEAT_RESTORE_ORIG_SCREEN
2515# ifdef FEAT_RESTORE_ORIG_SCREEN
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002516static ConsoleBuffer g_cbOrig = { 0 };
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002517# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002518static ConsoleBuffer g_cbNonTermcap = { 0 };
2519static ConsoleBuffer g_cbTermcap = { 0 };
2520
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002521# ifdef FEAT_TITLE
Bram Moolenaar071d4272004-06-13 20:20:40 +00002522char g_szOrigTitle[256] = { 0 };
Bram Moolenaar0f873732019-12-05 20:28:46 +01002523HWND g_hWnd = NULL; // also used in os_mswin.c
Bram Moolenaar071d4272004-06-13 20:20:40 +00002524static HICON g_hOrigIconSmall = NULL;
2525static HICON g_hOrigIcon = NULL;
2526static HICON g_hVimIcon = NULL;
2527static BOOL g_fCanChangeIcon = FALSE;
2528
Bram Moolenaar0f873732019-12-05 20:28:46 +01002529// ICON* are not defined in VC++ 4.0
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002530# ifndef ICON_SMALL
2531# define ICON_SMALL 0
2532# endif
2533# ifndef ICON_BIG
2534# define ICON_BIG 1
2535# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002536/*
2537 * GetConsoleIcon()
2538 * Description:
2539 * Attempts to retrieve the small icon and/or the big icon currently in
2540 * use by a given window.
2541 * Returns:
2542 * TRUE on success
2543 */
2544 static BOOL
2545GetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002546 HWND hWnd,
2547 HICON *phIconSmall,
2548 HICON *phIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002549{
2550 if (hWnd == NULL)
2551 return FALSE;
2552
2553 if (phIconSmall != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002554 *phIconSmall = (HICON)SendMessage(hWnd, WM_GETICON,
2555 (WPARAM)ICON_SMALL, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002556 if (phIcon != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002557 *phIcon = (HICON)SendMessage(hWnd, WM_GETICON,
2558 (WPARAM)ICON_BIG, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559 return TRUE;
2560}
2561
2562/*
2563 * SetConsoleIcon()
2564 * Description:
2565 * Attempts to change the small icon and/or the big icon currently in
2566 * use by a given window.
2567 * Returns:
2568 * TRUE on success
2569 */
2570 static BOOL
2571SetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002572 HWND hWnd,
2573 HICON hIconSmall,
2574 HICON hIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002575{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002576 if (hWnd == NULL)
2577 return FALSE;
2578
2579 if (hIconSmall != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002580 SendMessage(hWnd, WM_SETICON,
2581 (WPARAM)ICON_SMALL, (LPARAM)hIconSmall);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002582 if (hIcon != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002583 SendMessage(hWnd, WM_SETICON,
2584 (WPARAM)ICON_BIG, (LPARAM) hIcon);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002585 return TRUE;
2586}
2587
2588/*
2589 * SaveConsoleTitleAndIcon()
2590 * Description:
2591 * Saves the current console window title in g_szOrigTitle, for later
2592 * restoration. Also, attempts to obtain a handle to the console window,
2593 * and use it to save the small and big icons currently in use by the
2594 * console window. This is not always possible on some versions of Windows;
2595 * nor is it possible when running Vim remotely using Telnet (since the
2596 * console window the user sees is owned by a remote process).
2597 */
2598 static void
2599SaveConsoleTitleAndIcon(void)
2600{
Bram Moolenaar0f873732019-12-05 20:28:46 +01002601 // Save the original title.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002602 if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle)))
2603 return;
2604
2605 /*
2606 * Obtain a handle to the console window using GetConsoleWindow() from
2607 * KERNEL32.DLL; we need to handle in order to change the window icon.
2608 * This function only exists on NT-based Windows, starting with Windows
2609 * 2000. On older operating systems, we can't change the window icon
2610 * anyway.
2611 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002612 g_hWnd = GetConsoleWindow();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002613 if (g_hWnd == NULL)
2614 return;
2615
Bram Moolenaar0f873732019-12-05 20:28:46 +01002616 // Save the original console window icon.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002617 GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon);
2618 if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL)
2619 return;
2620
Bram Moolenaar0f873732019-12-05 20:28:46 +01002621 // Extract the first icon contained in the Vim executable.
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02002622 if (mch_icon_load((HANDLE *)&g_hVimIcon) == FAIL || g_hVimIcon == NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002623 g_hVimIcon = ExtractIcon(NULL, (LPCSTR)exe_name, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002624 if (g_hVimIcon != NULL)
2625 g_fCanChangeIcon = TRUE;
2626}
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002627# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002628
2629static int g_fWindInitCalled = FALSE;
2630static int g_fTermcapMode = FALSE;
2631static CONSOLE_CURSOR_INFO g_cci;
2632static DWORD g_cmodein = 0;
2633static DWORD g_cmodeout = 0;
2634
2635/*
2636 * non-GUI version of mch_init().
2637 */
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002638 static void
2639mch_init_c(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002640{
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002641# ifndef FEAT_RESTORE_ORIG_SCREEN
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002642 CONSOLE_SCREEN_BUFFER_INFO csbi;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002643# endif
2644# ifndef __MINGW32__
Bram Moolenaar071d4272004-06-13 20:20:40 +00002645 extern int _fmode;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002646# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002647
Bram Moolenaar0f873732019-12-05 20:28:46 +01002648 // Silently handle invalid parameters to CRT functions
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002649 SET_INVALID_PARAM_HANDLER;
2650
Bram Moolenaar0f873732019-12-05 20:28:46 +01002651 // Let critical errors result in a failure, not in a dialog box. Required
2652 // for the timestamp test to work on removed floppies.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002653 SetErrorMode(SEM_FAILCRITICALERRORS);
2654
Bram Moolenaar0f873732019-12-05 20:28:46 +01002655 _fmode = O_BINARY; // we do our own CR-LF translation
Bram Moolenaar071d4272004-06-13 20:20:40 +00002656 out_flush();
2657
Bram Moolenaar0f873732019-12-05 20:28:46 +01002658 // Obtain handles for the standard Console I/O devices
Bram Moolenaar071d4272004-06-13 20:20:40 +00002659 if (read_cmd_fd == 0)
2660 g_hConIn = GetStdHandle(STD_INPUT_HANDLE);
2661 else
2662 create_conin();
2663 g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
2664
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002665# ifdef FEAT_RESTORE_ORIG_SCREEN
Bram Moolenaar0f873732019-12-05 20:28:46 +01002666 // Save the initial console buffer for later restoration
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002667 SaveConsoleBuffer(&g_cbOrig);
2668 g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002669# else
Bram Moolenaar0f873732019-12-05 20:28:46 +01002670 // Get current text attributes
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002671 GetConsoleScreenBufferInfo(g_hConOut, &csbi);
2672 g_attrCurrent = g_attrDefault = csbi.wAttributes;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002673# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002674 if (cterm_normal_fg_color == 0)
2675 cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
2676 if (cterm_normal_bg_color == 0)
2677 cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
2678
Bram Moolenaarbdace832019-03-02 10:13:42 +01002679 // Fg and Bg color index number at startup
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02002680 g_color_index_fg = g_attrDefault & 0xf;
2681 g_color_index_bg = (g_attrDefault >> 4) & 0xf;
2682
Bram Moolenaar0f873732019-12-05 20:28:46 +01002683 // set termcap codes to current text attributes
Bram Moolenaar071d4272004-06-13 20:20:40 +00002684 update_tcap(g_attrCurrent);
2685
2686 GetConsoleCursorInfo(g_hConOut, &g_cci);
2687 GetConsoleMode(g_hConIn, &g_cmodein);
2688 GetConsoleMode(g_hConOut, &g_cmodeout);
2689
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002690# ifdef FEAT_TITLE
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691 SaveConsoleTitleAndIcon();
2692 /*
2693 * Set both the small and big icons of the console window to Vim's icon.
2694 * Note that Vim presently only has one size of icon (32x32), but it
2695 * automatically gets scaled down to 16x16 when setting the small icon.
2696 */
2697 if (g_fCanChangeIcon)
2698 SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002699# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002700
2701 ui_get_shellsize();
2702
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002703# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00002704 fdDump = fopen("dump", "wt");
2705
2706 if (fdDump)
2707 {
2708 time_t t;
2709
2710 time(&t);
2711 fputs(ctime(&t), fdDump);
2712 fflush(fdDump);
2713 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002714# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002715
2716 g_fWindInitCalled = TRUE;
2717
Bram Moolenaar071d4272004-06-13 20:20:40 +00002718 g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002719
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002720# ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002721 win_clip_init();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002722# endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01002723
Bram Moolenaar81ccbf12020-04-15 21:05:30 +02002724 vtp_flag_init();
Bram Moolenaarcafafb32018-02-22 21:07:09 +01002725 vtp_init();
Bram Moolenaar06b7b582020-05-30 17:49:25 +02002726 wt_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002727}
2728
2729/*
2730 * non-GUI version of mch_exit().
2731 * Shut down and exit with status `r'
2732 * Careful: mch_exit() may be called before mch_init()!
2733 */
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002734 static void
2735mch_exit_c(int r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002736{
Bram Moolenaar955f1982017-02-05 15:10:51 +01002737 exiting = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002738
Bram Moolenaarcafafb32018-02-22 21:07:09 +01002739 vtp_exit();
2740
Bram Moolenaar955f1982017-02-05 15:10:51 +01002741 stoptermcap();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002742 if (g_fWindInitCalled)
2743 settmode(TMODE_COOK);
2744
Bram Moolenaar0f873732019-12-05 20:28:46 +01002745 ml_close_all(TRUE); // remove all memfiles
Bram Moolenaar071d4272004-06-13 20:20:40 +00002746
2747 if (g_fWindInitCalled)
2748 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002749# ifdef FEAT_TITLE
Bram Moolenaar40385db2018-08-07 22:31:44 +02002750 mch_restore_title(SAVE_RESTORE_BOTH);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002751 /*
2752 * Restore both the small and big icons of the console window to
2753 * what they were at startup. Don't do this when the window is
2754 * closed, Vim would hang here.
2755 */
2756 if (g_fCanChangeIcon && !g_fForceExit)
2757 SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002758# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002759
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002760# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00002761 if (fdDump)
2762 {
2763 time_t t;
2764
2765 time(&t);
2766 fputs(ctime(&t), fdDump);
2767 fclose(fdDump);
2768 }
2769 fdDump = NULL;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002770# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002771 }
2772
2773 SetConsoleCursorInfo(g_hConOut, &g_cci);
2774 SetConsoleMode(g_hConIn, g_cmodein);
2775 SetConsoleMode(g_hConOut, g_cmodeout);
2776
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002777# ifdef DYNAMIC_GETTEXT
Bram Moolenaar071d4272004-06-13 20:20:40 +00002778 dyn_libintl_end();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002779# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002780
2781 exit(r);
2782}
Bram Moolenaar0f873732019-12-05 20:28:46 +01002783#endif // !FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002784
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002785 void
2786mch_init(void)
2787{
2788#ifdef VIMDLL
2789 if (gui.starting)
2790 mch_init_g();
2791 else
2792 mch_init_c();
2793#elif defined(FEAT_GUI_MSWIN)
2794 mch_init_g();
2795#else
2796 mch_init_c();
2797#endif
2798}
2799
2800 void
2801mch_exit(int r)
2802{
Bram Moolenaar173d8412020-04-19 14:02:26 +02002803#ifdef FEAT_NETBEANS_INTG
2804 netbeans_send_disconnect();
2805#endif
2806
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002807#ifdef VIMDLL
Bram Moolenaar294d9bf2019-05-23 20:12:46 +02002808 if (gui.in_use || gui.starting)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002809 mch_exit_g(r);
2810 else
2811 mch_exit_c(r);
2812#elif defined(FEAT_GUI_MSWIN)
2813 mch_exit_g(r);
2814#else
2815 mch_exit_c(r);
2816#endif
2817}
2818
Bram Moolenaar071d4272004-06-13 20:20:40 +00002819/*
2820 * Do we have an interactive window?
2821 */
2822 int
2823mch_check_win(
Bram Moolenaar1266d672017-02-01 13:43:36 +01002824 int argc UNUSED,
2825 char **argv UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002826{
2827 get_exe_name();
2828
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002829#if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01002830 return OK; // GUI always has a tty
Bram Moolenaar071d4272004-06-13 20:20:40 +00002831#else
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002832# ifdef VIMDLL
2833 if (gui.in_use)
2834 return OK;
2835# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002836 if (isatty(1))
2837 return OK;
2838 return FAIL;
2839#endif
2840}
2841
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002842/*
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01002843 * Set the case of the file name, if it already exists.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002844 * When "len" is > 0, also expand short to long filenames.
2845 */
2846 void
2847fname_case(
2848 char_u *name,
2849 int len)
2850{
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01002851 int flen;
2852 WCHAR *p;
2853 WCHAR buf[_MAX_PATH + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002854
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +00002855 flen = (int)STRLEN(name);
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002856 if (flen == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002857 return;
2858
2859 slash_adjust(name);
2860
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01002861 p = enc_to_utf16(name, NULL);
2862 if (p == NULL)
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002863 return;
2864
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01002865 if (GetLongPathNameW(p, buf, _MAX_PATH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002866 {
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01002867 char_u *q = utf16_to_enc(buf, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002868
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01002869 if (q != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002870 {
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01002871 if (len > 0 || flen >= (int)STRLEN(q))
2872 vim_strncpy(name, q, (len > 0) ? len - 1 : flen);
2873 vim_free(q);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002874 }
2875 }
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01002876 vim_free(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002877}
2878
2879
2880/*
2881 * Insert user name in s[len].
2882 */
2883 int
2884mch_get_user_name(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002885 char_u *s,
2886 int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002887{
Bram Moolenaar0f873732019-12-05 20:28:46 +01002888 WCHAR wszUserName[256 + 1]; // UNLEN is 256
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002889 DWORD wcch = sizeof(wszUserName) / sizeof(WCHAR);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002891 if (GetUserNameW(wszUserName, &wcch))
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01002892 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002893 char_u *p = utf16_to_enc(wszUserName, NULL);
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01002894
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002895 if (p != NULL)
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01002896 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002897 vim_strncpy(s, p, len - 1);
2898 vim_free(p);
2899 return OK;
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01002900 }
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01002901 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002902 s[0] = NUL;
2903 return FAIL;
2904}
2905
2906
2907/*
2908 * Insert host name in s[len].
2909 */
2910 void
2911mch_get_host_name(
2912 char_u *s,
2913 int len)
2914{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002915 WCHAR wszHostName[256 + 1];
2916 DWORD wcch = sizeof(wszHostName) / sizeof(WCHAR);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002917
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002918 if (GetComputerNameW(wszHostName, &wcch))
Bram Moolenaar2cc87382013-12-11 18:21:45 +01002919 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002920 char_u *p = utf16_to_enc(wszHostName, NULL);
Bram Moolenaar2cc87382013-12-11 18:21:45 +01002921
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002922 if (p != NULL)
Bram Moolenaar2cc87382013-12-11 18:21:45 +01002923 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002924 vim_strncpy(s, p, len - 1);
2925 vim_free(p);
2926 return;
Bram Moolenaar2cc87382013-12-11 18:21:45 +01002927 }
Bram Moolenaar2cc87382013-12-11 18:21:45 +01002928 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002929}
2930
2931
2932/*
2933 * return process ID
2934 */
2935 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002936mch_get_pid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002937{
2938 return (long)GetCurrentProcessId();
2939}
2940
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002941/*
2942 * return TRUE if process "pid" is still running
2943 */
2944 int
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002945mch_process_running(long pid)
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002946{
2947 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, 0, (DWORD)pid);
2948 DWORD status = 0;
2949 int ret = FALSE;
2950
2951 if (hProcess == NULL)
2952 return FALSE; // might not have access
2953 if (GetExitCodeProcess(hProcess, &status) )
2954 ret = status == STILL_ACTIVE;
2955 CloseHandle(hProcess);
2956 return ret;
2957}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002958
2959/*
2960 * Get name of current directory into buffer 'buf' of length 'len' bytes.
2961 * Return OK for success, FAIL for failure.
2962 */
2963 int
2964mch_dirname(
2965 char_u *buf,
2966 int len)
2967{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002968 WCHAR wbuf[_MAX_PATH + 1];
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02002969
Bram Moolenaar071d4272004-06-13 20:20:40 +00002970 /*
2971 * Originally this was:
2972 * return (getcwd(buf, len) != NULL ? OK : FAIL);
2973 * But the Win32s known bug list says that getcwd() doesn't work
2974 * so use the Win32 system call instead. <Negri>
2975 */
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002976 if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002977 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002978 WCHAR wcbuf[_MAX_PATH + 1];
2979 char_u *p = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002980
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002981 if (GetLongPathNameW(wbuf, wcbuf, _MAX_PATH) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002982 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002983 p = utf16_to_enc(wcbuf, NULL);
2984 if (STRLEN(p) >= (size_t)len)
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02002985 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002986 // long path name is too long, fall back to short one
Bram Moolenaar071d4272004-06-13 20:20:40 +00002987 vim_free(p);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002988 p = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002989 }
2990 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002991 if (p == NULL)
2992 p = utf16_to_enc(wbuf, NULL);
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02002993
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002994 if (p != NULL)
2995 {
2996 vim_strncpy(buf, p, len - 1);
2997 vim_free(p);
2998 return OK;
2999 }
3000 }
3001 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003002}
3003
3004/*
Bram Moolenaarffa22202013-11-21 12:34:11 +01003005 * Get file permissions for "name".
3006 * Return mode_t or -1 for error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003007 */
3008 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003009mch_getperm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003010{
Bram Moolenaar8767f522016-07-01 17:17:39 +02003011 stat_T st;
Bram Moolenaarffa22202013-11-21 12:34:11 +01003012 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003014 n = mch_stat((char *)name, &st);
Bram Moolenaar78cf3f02014-01-10 18:16:07 +01003015 return n == 0 ? (long)(unsigned short)st.st_mode : -1L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003016}
3017
3018
3019/*
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003020 * Set file permission for "name" to "perm".
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003021 *
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003022 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003023 */
3024 int
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003025mch_setperm(char_u *name, long perm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003026{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003027 long n;
3028 WCHAR *p;
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003029
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003030 p = enc_to_utf16(name, NULL);
3031 if (p == NULL)
3032 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003033
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003034 n = _wchmod(p, perm);
3035 vim_free(p);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003036 if (n == -1)
3037 return FAIL;
3038
3039 win32_set_archive(name);
3040
3041 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003042}
3043
3044/*
3045 * Set hidden flag for "name".
3046 */
3047 void
3048mch_hide(char_u *name)
3049{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003050 int attrs = win32_getattrs(name);
3051 if (attrs == -1)
3052 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003054 attrs |= FILE_ATTRIBUTE_HIDDEN;
3055 win32_setattrs(name, attrs);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003056}
3057
3058/*
Bram Moolenaar8a52ba72015-11-02 14:45:56 +01003059 * Return TRUE if file "name" exists and is hidden.
3060 */
3061 int
3062mch_ishidden(char_u *name)
3063{
3064 int f = win32_getattrs(name);
3065
3066 if (f == -1)
Bram Moolenaar0f873732019-12-05 20:28:46 +01003067 return FALSE; // file does not exist at all
Bram Moolenaar8a52ba72015-11-02 14:45:56 +01003068
3069 return (f & FILE_ATTRIBUTE_HIDDEN) != 0;
3070}
3071
3072/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003073 * return TRUE if "name" is a directory
3074 * return FALSE if "name" is not a directory or upon error
3075 */
3076 int
3077mch_isdir(char_u *name)
3078{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003079 int f = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003080
3081 if (f == -1)
Bram Moolenaar0f873732019-12-05 20:28:46 +01003082 return FALSE; // file does not exist at all
Bram Moolenaar071d4272004-06-13 20:20:40 +00003083
3084 return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
3085}
3086
3087/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003088 * return TRUE if "name" is a directory, NOT a symlink to a directory
3089 * return FALSE if "name" is not a directory
3090 * return FALSE for error
3091 */
3092 int
3093mch_isrealdir(char_u *name)
3094{
3095 return mch_isdir(name) && !mch_is_symbolic_link(name);
3096}
3097
3098/*
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003099 * Create directory "name".
3100 * Return 0 on success, -1 on error.
3101 */
3102 int
3103mch_mkdir(char_u *name)
3104{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003105 WCHAR *p;
3106 int retval;
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003107
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003108 p = enc_to_utf16(name, NULL);
3109 if (p == NULL)
3110 return -1;
3111 retval = _wmkdir(p);
3112 vim_free(p);
3113 return retval;
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003114}
3115
3116/*
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003117 * Delete directory "name".
3118 * Return 0 on success, -1 on error.
3119 */
3120 int
3121mch_rmdir(char_u *name)
3122{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003123 WCHAR *p;
3124 int retval;
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003125
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003126 p = enc_to_utf16(name, NULL);
3127 if (p == NULL)
3128 return -1;
3129 retval = _wrmdir(p);
3130 vim_free(p);
3131 return retval;
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003132}
3133
3134/*
Bram Moolenaar03f48552006-02-28 23:52:23 +00003135 * Return TRUE if file "fname" has more than one link.
3136 */
3137 int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003138mch_is_hard_link(char_u *fname)
Bram Moolenaar03f48552006-02-28 23:52:23 +00003139{
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003140 BY_HANDLE_FILE_INFORMATION info;
3141
3142 return win32_fileinfo(fname, &info) == FILEINFO_OK
3143 && info.nNumberOfLinks > 1;
3144}
3145
3146/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003147 * Return TRUE if "name" is a symbolic link (or a junction).
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003148 */
3149 int
Bram Moolenaar203258c2016-01-17 22:15:16 +01003150mch_is_symbolic_link(char_u *name)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003151{
3152 HANDLE hFind;
3153 int res = FALSE;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003154 DWORD fileFlags = 0, reparseTag = 0;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003155 WCHAR *wn;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003156 WIN32_FIND_DATAW findDataW;
3157
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003158 wn = enc_to_utf16(name, NULL);
3159 if (wn == NULL)
3160 return FALSE;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003161
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003162 hFind = FindFirstFileW(wn, &findDataW);
3163 vim_free(wn);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003164 if (hFind != INVALID_HANDLE_VALUE)
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003165 {
3166 fileFlags = findDataW.dwFileAttributes;
3167 reparseTag = findDataW.dwReserved0;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003168 FindClose(hFind);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003169 }
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003170
3171 if ((fileFlags & FILE_ATTRIBUTE_REPARSE_POINT)
Bram Moolenaar203258c2016-01-17 22:15:16 +01003172 && (reparseTag == IO_REPARSE_TAG_SYMLINK
3173 || reparseTag == IO_REPARSE_TAG_MOUNT_POINT))
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003174 res = TRUE;
3175
3176 return res;
3177}
3178
3179/*
3180 * Return TRUE if file "fname" has more than one link or if it is a symbolic
3181 * link.
3182 */
3183 int
3184mch_is_linked(char_u *fname)
3185{
3186 if (mch_is_hard_link(fname) || mch_is_symbolic_link(fname))
3187 return TRUE;
3188 return FALSE;
3189}
3190
3191/*
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003192 * Get the by-handle-file-information for "fname".
3193 * Returns FILEINFO_OK when OK.
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003194 * Returns FILEINFO_ENC_FAIL when enc_to_utf16() failed.
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003195 * Returns FILEINFO_READ_FAIL when CreateFile() failed.
3196 * Returns FILEINFO_INFO_FAIL when GetFileInformationByHandle() failed.
3197 */
3198 int
3199win32_fileinfo(char_u *fname, BY_HANDLE_FILE_INFORMATION *info)
3200{
Bram Moolenaar03f48552006-02-28 23:52:23 +00003201 HANDLE hFile;
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003202 int res = FILEINFO_READ_FAIL;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003203 WCHAR *wn;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003204
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003205 wn = enc_to_utf16(fname, NULL);
3206 if (wn == NULL)
3207 return FILEINFO_ENC_FAIL;
3208
3209 hFile = CreateFileW(wn, // file name
3210 GENERIC_READ, // access mode
3211 FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
3212 NULL, // security descriptor
3213 OPEN_EXISTING, // creation disposition
3214 FILE_FLAG_BACKUP_SEMANTICS, // file attributes
3215 NULL); // handle to template file
3216 vim_free(wn);
Bram Moolenaar03f48552006-02-28 23:52:23 +00003217
3218 if (hFile != INVALID_HANDLE_VALUE)
3219 {
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003220 if (GetFileInformationByHandle(hFile, info) != 0)
3221 res = FILEINFO_OK;
3222 else
3223 res = FILEINFO_INFO_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003224 CloseHandle(hFile);
3225 }
3226
Bram Moolenaar03f48552006-02-28 23:52:23 +00003227 return res;
3228}
3229
3230/*
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003231 * get file attributes for `name'
3232 * -1 : error
3233 * else FILE_ATTRIBUTE_* defined in winnt.h
3234 */
Bram Moolenaarffa22202013-11-21 12:34:11 +01003235 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003236win32_getattrs(char_u *name)
3237{
3238 int attr;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003239 WCHAR *p;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003240
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003241 p = enc_to_utf16(name, NULL);
3242 if (p == NULL)
3243 return INVALID_FILE_ATTRIBUTES;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003244
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003245 attr = GetFileAttributesW(p);
3246 vim_free(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003247
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003248 return attr;
3249}
3250
3251/*
3252 * set file attributes for `name' to `attrs'
3253 *
3254 * return -1 for failure, 0 otherwise
3255 */
Bram Moolenaar6dff58f2018-09-30 21:43:26 +02003256 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003257win32_setattrs(char_u *name, int attrs)
3258{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003259 int res;
3260 WCHAR *p;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003261
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003262 p = enc_to_utf16(name, NULL);
3263 if (p == NULL)
3264 return -1;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003265
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003266 res = SetFileAttributesW(p, attrs);
3267 vim_free(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003268
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003269 return res ? 0 : -1;
3270}
3271
3272/*
3273 * Set archive flag for "name".
3274 */
Bram Moolenaar6dff58f2018-09-30 21:43:26 +02003275 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003276win32_set_archive(char_u *name)
3277{
3278 int attrs = win32_getattrs(name);
3279 if (attrs == -1)
3280 return -1;
3281
3282 attrs |= FILE_ATTRIBUTE_ARCHIVE;
3283 return win32_setattrs(name, attrs);
3284}
3285
3286/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003287 * Return TRUE if file or directory "name" is writable (not readonly).
3288 * Strange semantics of Win32: a readonly directory is writable, but you can't
3289 * delete a file. Let's say this means it is writable.
3290 */
3291 int
3292mch_writable(char_u *name)
3293{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003294 int attrs = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003295
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003296 return (attrs != -1 && (!(attrs & FILE_ATTRIBUTE_READONLY)
3297 || (attrs & FILE_ATTRIBUTE_DIRECTORY)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003298}
3299
Bram Moolenaar071d4272004-06-13 20:20:40 +00003300/*
Bram Moolenaar43663192017-03-05 14:29:12 +01003301 * Return TRUE if "name" can be executed, FALSE if not.
Bram Moolenaar77b77102015-03-21 22:18:41 +01003302 * If "use_path" is FALSE only check if "name" is executable.
Bram Moolenaar43663192017-03-05 14:29:12 +01003303 * When returning TRUE and "path" is not NULL save the path and set "*path" to
3304 * the allocated memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003305 */
3306 int
Bram Moolenaar77b77102015-03-21 22:18:41 +01003307mch_can_exe(char_u *name, char_u **path, int use_path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003308{
Bram Moolenaar86621892019-03-30 21:51:28 +01003309 // WinNT and later can use _MAX_PATH wide characters for a pathname, which
3310 // means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
3311 // UTF-8.
3312 char_u buf[_MAX_PATH * 3];
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003313 int len = (int)STRLEN(name);
Bram Moolenaar82956662018-10-06 15:18:45 +02003314 char_u *p, *saved;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003315
Bram Moolenaar86621892019-03-30 21:51:28 +01003316 if (len >= sizeof(buf)) // safety check
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003317 return FALSE;
3318
Bram Moolenaar86621892019-03-30 21:51:28 +01003319 // Try using the name directly when a Unix-shell like 'shell'.
Bram Moolenaar82956662018-10-06 15:18:45 +02003320 if (strstr((char *)gettail(p_sh), "sh") != NULL)
Bram Moolenaar43663192017-03-05 14:29:12 +01003321 if (executable_exists((char *)name, path, use_path))
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003322 return TRUE;
3323
3324 /*
3325 * Loop over all extensions in $PATHEXT.
3326 */
Bram Moolenaar82956662018-10-06 15:18:45 +02003327 p = mch_getenv("PATHEXT");
3328 if (p == NULL)
3329 p = (char_u *)".com;.exe;.bat;.cmd";
3330 saved = vim_strsave(p);
3331 if (saved == NULL)
3332 return FALSE;
3333 p = saved;
3334 while (*p)
3335 {
3336 char_u *tmp = vim_strchr(p, ';');
3337
3338 if (tmp != NULL)
3339 *tmp = NUL;
3340 if (_stricoll((char *)name + len - STRLEN(p), (char *)p) == 0
3341 && executable_exists((char *)name, path, use_path))
3342 {
3343 vim_free(saved);
3344 return TRUE;
3345 }
3346 if (tmp == NULL)
3347 break;
3348 p = tmp + 1;
3349 }
3350 vim_free(saved);
3351
Bram Moolenaar86621892019-03-30 21:51:28 +01003352 vim_strncpy(buf, name, sizeof(buf) - 1);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003353 p = mch_getenv("PATHEXT");
3354 if (p == NULL)
3355 p = (char_u *)".com;.exe;.bat;.cmd";
3356 while (*p)
3357 {
3358 if (p[0] == '.' && (p[1] == NUL || p[1] == ';'))
3359 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01003360 // A single "." means no extension is added.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003361 buf[len] = NUL;
3362 ++p;
3363 if (*p)
3364 ++p;
3365 }
3366 else
Bram Moolenaar86621892019-03-30 21:51:28 +01003367 copy_option_part(&p, buf + len, sizeof(buf) - len, ";");
Bram Moolenaar43663192017-03-05 14:29:12 +01003368 if (executable_exists((char *)buf, path, use_path))
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003369 return TRUE;
3370 }
3371 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003372}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003373
3374/*
3375 * Check what "name" is:
3376 * NODE_NORMAL: file or directory (or doesn't exist)
3377 * NODE_WRITABLE: writable device, socket, fifo, etc.
3378 * NODE_OTHER: non-writable things
3379 */
3380 int
3381mch_nodetype(char_u *name)
3382{
3383 HANDLE hFile;
3384 int type;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003385 WCHAR *wn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003386
Bram Moolenaar0f873732019-12-05 20:28:46 +01003387 // We can't open a file with a name "\\.\con" or "\\.\prn" and trying to
3388 // read from it later will cause Vim to hang. Thus return NODE_WRITABLE
3389 // here.
Bram Moolenaar043545e2006-10-10 16:44:07 +00003390 if (STRNCMP(name, "\\\\.\\", 4) == 0)
3391 return NODE_WRITABLE;
3392
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003393 wn = enc_to_utf16(name, NULL);
3394 if (wn == NULL)
3395 return NODE_NORMAL;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003396
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003397 hFile = CreateFileW(wn, // file name
3398 GENERIC_WRITE, // access mode
3399 0, // share mode
3400 NULL, // security descriptor
3401 OPEN_EXISTING, // creation disposition
3402 0, // file attributes
3403 NULL); // handle to template file
3404 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003405 if (hFile == INVALID_HANDLE_VALUE)
3406 return NODE_NORMAL;
3407
3408 type = GetFileType(hFile);
3409 CloseHandle(hFile);
3410 if (type == FILE_TYPE_CHAR)
3411 return NODE_WRITABLE;
3412 if (type == FILE_TYPE_DISK)
3413 return NODE_NORMAL;
3414 return NODE_OTHER;
3415}
3416
3417#ifdef HAVE_ACL
3418struct my_acl
3419{
3420 PSECURITY_DESCRIPTOR pSecurityDescriptor;
3421 PSID pSidOwner;
3422 PSID pSidGroup;
3423 PACL pDacl;
3424 PACL pSacl;
3425};
3426#endif
3427
3428/*
3429 * Return a pointer to the ACL of file "fname" in allocated memory.
3430 * Return NULL if the ACL is not available for whatever reason.
3431 */
3432 vim_acl_T
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003433mch_get_acl(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003434{
3435#ifndef HAVE_ACL
3436 return (vim_acl_T)NULL;
3437#else
3438 struct my_acl *p = NULL;
Bram Moolenaar27515922013-06-29 15:36:26 +02003439 DWORD err;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003440
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003441 p = ALLOC_CLEAR_ONE(struct my_acl);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003442 if (p != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003443 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003444 WCHAR *wn;
Bram Moolenaar27515922013-06-29 15:36:26 +02003445
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003446 wn = enc_to_utf16(fname, NULL);
3447 if (wn == NULL)
3448 return NULL;
3449
3450 // Try to retrieve the entire security descriptor.
3451 err = GetNamedSecurityInfoW(
3452 wn, // Abstract filename
3453 SE_FILE_OBJECT, // File Object
3454 OWNER_SECURITY_INFORMATION |
3455 GROUP_SECURITY_INFORMATION |
3456 DACL_SECURITY_INFORMATION |
3457 SACL_SECURITY_INFORMATION,
3458 &p->pSidOwner, // Ownership information.
3459 &p->pSidGroup, // Group membership.
3460 &p->pDacl, // Discretionary information.
3461 &p->pSacl, // For auditing purposes.
3462 &p->pSecurityDescriptor);
3463 if (err == ERROR_ACCESS_DENIED ||
3464 err == ERROR_PRIVILEGE_NOT_HELD)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003465 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003466 // Retrieve only DACL.
3467 (void)GetNamedSecurityInfoW(
3468 wn,
3469 SE_FILE_OBJECT,
3470 DACL_SECURITY_INFORMATION,
3471 NULL,
3472 NULL,
3473 &p->pDacl,
3474 NULL,
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003475 &p->pSecurityDescriptor);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003476 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003477 if (p->pSecurityDescriptor == NULL)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003478 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003479 mch_free_acl((vim_acl_T)p);
3480 p = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003481 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003482 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003483 }
3484
3485 return (vim_acl_T)p;
3486#endif
3487}
3488
Bram Moolenaar27515922013-06-29 15:36:26 +02003489#ifdef HAVE_ACL
3490/*
3491 * Check if "acl" contains inherited ACE.
3492 */
3493 static BOOL
3494is_acl_inherited(PACL acl)
3495{
3496 DWORD i;
3497 ACL_SIZE_INFORMATION acl_info;
3498 PACCESS_ALLOWED_ACE ace;
3499
3500 acl_info.AceCount = 0;
3501 GetAclInformation(acl, &acl_info, sizeof(acl_info), AclSizeInformation);
3502 for (i = 0; i < acl_info.AceCount; i++)
3503 {
3504 GetAce(acl, i, (LPVOID *)&ace);
3505 if (ace->Header.AceFlags & INHERITED_ACE)
3506 return TRUE;
3507 }
3508 return FALSE;
3509}
3510#endif
3511
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512/*
3513 * Set the ACL of file "fname" to "acl" (unless it's NULL).
3514 * Errors are ignored.
3515 * This must only be called with "acl" equal to what mch_get_acl() returned.
3516 */
3517 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003518mch_set_acl(char_u *fname, vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519{
3520#ifdef HAVE_ACL
3521 struct my_acl *p = (struct my_acl *)acl;
Bram Moolenaar27515922013-06-29 15:36:26 +02003522 SECURITY_INFORMATION sec_info = 0;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003523 WCHAR *wn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003524
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003525 if (p == NULL)
3526 return;
3527
3528 wn = enc_to_utf16(fname, NULL);
3529 if (wn == NULL)
3530 return;
3531
3532 // Set security flags
3533 if (p->pSidOwner)
3534 sec_info |= OWNER_SECURITY_INFORMATION;
3535 if (p->pSidGroup)
3536 sec_info |= GROUP_SECURITY_INFORMATION;
3537 if (p->pDacl)
Bram Moolenaar27515922013-06-29 15:36:26 +02003538 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003539 sec_info |= DACL_SECURITY_INFORMATION;
3540 // Do not inherit its parent's DACL.
3541 // If the DACL is inherited, Cygwin permissions would be changed.
3542 if (!is_acl_inherited(p->pDacl))
3543 sec_info |= PROTECTED_DACL_SECURITY_INFORMATION;
Bram Moolenaar27515922013-06-29 15:36:26 +02003544 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003545 if (p->pSacl)
3546 sec_info |= SACL_SECURITY_INFORMATION;
3547
3548 (void)SetNamedSecurityInfoW(
3549 wn, // Abstract filename
3550 SE_FILE_OBJECT, // File Object
3551 sec_info,
3552 p->pSidOwner, // Ownership information.
3553 p->pSidGroup, // Group membership.
3554 p->pDacl, // Discretionary information.
3555 p->pSacl // For auditing purposes.
3556 );
3557 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003558#endif
3559}
3560
3561 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003562mch_free_acl(vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003563{
3564#ifdef HAVE_ACL
3565 struct my_acl *p = (struct my_acl *)acl;
3566
3567 if (p != NULL)
3568 {
3569 LocalFree(p->pSecurityDescriptor); // Free the memory just in case
3570 vim_free(p);
3571 }
3572#endif
3573}
3574
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003575#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003576
3577/*
3578 * handler for ctrl-break, ctrl-c interrupts, and fatal events.
3579 */
3580 static BOOL WINAPI
3581handler_routine(
3582 DWORD dwCtrlType)
3583{
Bram Moolenaar589b1102017-08-12 16:39:05 +02003584 INPUT_RECORD ir;
3585 DWORD out;
3586
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587 switch (dwCtrlType)
3588 {
3589 case CTRL_C_EVENT:
3590 if (ctrl_c_interrupts)
3591 g_fCtrlCPressed = TRUE;
3592 return TRUE;
3593
3594 case CTRL_BREAK_EVENT:
3595 g_fCBrkPressed = TRUE;
Bram Moolenaar589b1102017-08-12 16:39:05 +02003596 ctrl_break_was_pressed = TRUE;
Bram Moolenaar0f873732019-12-05 20:28:46 +01003597 // ReadConsoleInput is blocking, send a key event to continue.
Bram Moolenaar589b1102017-08-12 16:39:05 +02003598 ir.EventType = KEY_EVENT;
3599 ir.Event.KeyEvent.bKeyDown = TRUE;
3600 ir.Event.KeyEvent.wRepeatCount = 1;
3601 ir.Event.KeyEvent.wVirtualKeyCode = VK_CANCEL;
3602 ir.Event.KeyEvent.wVirtualScanCode = 0;
3603 ir.Event.KeyEvent.dwControlKeyState = 0;
3604 ir.Event.KeyEvent.uChar.UnicodeChar = 0;
3605 WriteConsoleInput(g_hConIn, &ir, 1, &out);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003606 return TRUE;
3607
Bram Moolenaar0f873732019-12-05 20:28:46 +01003608 // fatal events: shut down gracefully
Bram Moolenaar071d4272004-06-13 20:20:40 +00003609 case CTRL_CLOSE_EVENT:
3610 case CTRL_LOGOFF_EVENT:
3611 case CTRL_SHUTDOWN_EVENT:
3612 windgoto((int)Rows - 1, 0);
3613 g_fForceExit = TRUE;
3614
Bram Moolenaar0fde2902008-03-16 13:54:13 +00003615 vim_snprintf((char *)IObuff, IOSIZE, _("Vim: Caught %s event\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616 (dwCtrlType == CTRL_CLOSE_EVENT
3617 ? _("close")
3618 : dwCtrlType == CTRL_LOGOFF_EVENT
3619 ? _("logoff")
3620 : _("shutdown")));
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003621# ifdef DEBUG
Bram Moolenaar071d4272004-06-13 20:20:40 +00003622 OutputDebugString(IObuff);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003623# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003624
Bram Moolenaar0f873732019-12-05 20:28:46 +01003625 preserve_exit(); // output IObuff, preserve files and exit
Bram Moolenaar071d4272004-06-13 20:20:40 +00003626
Bram Moolenaar0f873732019-12-05 20:28:46 +01003627 return TRUE; // not reached
Bram Moolenaar071d4272004-06-13 20:20:40 +00003628
3629 default:
3630 return FALSE;
3631 }
3632}
3633
3634
3635/*
3636 * set the tty in (raw) ? "raw" : "cooked" mode
3637 */
3638 void
Bram Moolenaar26e86442020-05-17 14:06:16 +02003639mch_settmode(tmode_T tmode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003640{
3641 DWORD cmodein;
3642 DWORD cmodeout;
3643 BOOL bEnableHandler;
3644
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003645# ifdef VIMDLL
3646 if (gui.in_use)
3647 return;
3648# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003649 GetConsoleMode(g_hConIn, &cmodein);
3650 GetConsoleMode(g_hConOut, &cmodeout);
3651 if (tmode == TMODE_RAW)
3652 {
3653 cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3654 ENABLE_ECHO_INPUT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003655 if (g_fMouseActive)
3656 cmodein |= ENABLE_MOUSE_INPUT;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01003657 cmodeout &= ~(
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003658# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar0f873732019-12-05 20:28:46 +01003659 // Do not turn off the ENABLE_PROCESSED_OUTPUT flag when using
3660 // VTP.
Bram Moolenaarcafafb32018-02-22 21:07:09 +01003661 ((vtp_working) ? 0 : ENABLE_PROCESSED_OUTPUT) |
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003662# else
Bram Moolenaarcafafb32018-02-22 21:07:09 +01003663 ENABLE_PROCESSED_OUTPUT |
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003664# endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01003665 ENABLE_WRAP_AT_EOL_OUTPUT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003666 bEnableHandler = TRUE;
3667 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01003668 else // cooked
Bram Moolenaar071d4272004-06-13 20:20:40 +00003669 {
3670 cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3671 ENABLE_ECHO_INPUT);
3672 cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3673 bEnableHandler = FALSE;
3674 }
3675 SetConsoleMode(g_hConIn, cmodein);
3676 SetConsoleMode(g_hConOut, cmodeout);
3677 SetConsoleCtrlHandler(handler_routine, bEnableHandler);
3678
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003679# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00003680 if (fdDump)
3681 {
3682 fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n",
3683 tmode == TMODE_RAW ? "raw" :
3684 tmode == TMODE_COOK ? "cooked" : "normal",
3685 cmodein, cmodeout);
3686 fflush(fdDump);
3687 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003688# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003689}
3690
3691
3692/*
3693 * Get the size of the current window in `Rows' and `Columns'
3694 * Return OK when size could be determined, FAIL otherwise.
3695 */
3696 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003697mch_get_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698{
3699 CONSOLE_SCREEN_BUFFER_INFO csbi;
3700
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003701# ifdef VIMDLL
3702 if (gui.in_use)
3703 return OK;
3704# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705 if (!g_fTermcapMode && g_cbTermcap.IsValid)
3706 {
3707 /*
3708 * For some reason, we are trying to get the screen dimensions
3709 * even though we are not in termcap mode. The 'Rows' and 'Columns'
3710 * variables are really intended to mean the size of Vim screen
3711 * while in termcap mode.
3712 */
3713 Rows = g_cbTermcap.Info.dwSize.Y;
3714 Columns = g_cbTermcap.Info.dwSize.X;
3715 }
3716 else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3717 {
3718 Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3719 Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3720 }
3721 else
3722 {
3723 Rows = 25;
3724 Columns = 80;
3725 }
3726 return OK;
3727}
3728
3729/*
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02003730 * Resize console buffer to 'COORD'
3731 */
3732 static void
3733ResizeConBuf(
3734 HANDLE hConsole,
3735 COORD coordScreen)
3736{
3737 if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
3738 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003739# ifdef MCH_WRITE_DUMP
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02003740 if (fdDump)
3741 {
3742 fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
3743 GetLastError());
3744 fflush(fdDump);
3745 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003746# endif
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02003747 }
3748}
3749
3750/*
3751 * Resize console window size to 'srWindowRect'
3752 */
3753 static void
3754ResizeWindow(
3755 HANDLE hConsole,
3756 SMALL_RECT srWindowRect)
3757{
3758 if (!SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect))
3759 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003760# ifdef MCH_WRITE_DUMP
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02003761 if (fdDump)
3762 {
3763 fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
3764 GetLastError());
3765 fflush(fdDump);
3766 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003767# endif
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02003768 }
3769}
3770
3771/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003772 * Set a console window to `xSize' * `ySize'
3773 */
3774 static void
3775ResizeConBufAndWindow(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003776 HANDLE hConsole,
3777 int xSize,
3778 int ySize)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003779{
Bram Moolenaar0f873732019-12-05 20:28:46 +01003780 CONSOLE_SCREEN_BUFFER_INFO csbi; // hold current console buffer info
3781 SMALL_RECT srWindowRect; // hold the new console size
Bram Moolenaar071d4272004-06-13 20:20:40 +00003782 COORD coordScreen;
Bram Moolenaarf49a6922019-07-15 20:37:05 +02003783 COORD cursor;
Bram Moolenaar2551c032018-08-23 22:38:31 +02003784 static int resized = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003785
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003786# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00003787 if (fdDump)
3788 {
3789 fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
3790 fflush(fdDump);
3791 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003792# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003793
Bram Moolenaar0f873732019-12-05 20:28:46 +01003794 // get the largest size we can size the console window to
Bram Moolenaar071d4272004-06-13 20:20:40 +00003795 coordScreen = GetLargestConsoleWindowSize(hConsole);
3796
Bram Moolenaar0f873732019-12-05 20:28:46 +01003797 // define the new console window size and scroll position
Bram Moolenaar071d4272004-06-13 20:20:40 +00003798 srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
3799 srWindowRect.Right = (SHORT) (min(xSize, coordScreen.X) - 1);
3800 srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
3801
3802 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3803 {
3804 int sx, sy;
3805
3806 sx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3807 sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3808 if (sy < ySize || sx < xSize)
3809 {
3810 /*
3811 * Increasing number of lines/columns, do buffer first.
3812 * Use the maximal size in x and y direction.
3813 */
3814 if (sy < ySize)
3815 coordScreen.Y = ySize;
3816 else
3817 coordScreen.Y = sy;
3818 if (sx < xSize)
3819 coordScreen.X = xSize;
3820 else
3821 coordScreen.X = sx;
3822 SetConsoleScreenBufferSize(hConsole, coordScreen);
3823 }
3824 }
3825
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02003826 // define the new console buffer size
Bram Moolenaar071d4272004-06-13 20:20:40 +00003827 coordScreen.X = xSize;
3828 coordScreen.Y = ySize;
3829
Bram Moolenaar2551c032018-08-23 22:38:31 +02003830 // In the new console call API, only the first time in reverse order
3831 if (!vtp_working || resized)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003832 {
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02003833 ResizeWindow(hConsole, srWindowRect);
3834 ResizeConBuf(hConsole, coordScreen);
3835 }
3836 else
3837 {
Bram Moolenaarf49a6922019-07-15 20:37:05 +02003838 // Workaround for a Windows 10 bug
3839 cursor.X = srWindowRect.Left;
3840 cursor.Y = srWindowRect.Top;
3841 SetConsoleCursorPosition(hConsole, cursor);
3842
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02003843 ResizeConBuf(hConsole, coordScreen);
3844 ResizeWindow(hConsole, srWindowRect);
Bram Moolenaar2551c032018-08-23 22:38:31 +02003845 resized = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003846 }
3847}
3848
3849
3850/*
3851 * Set the console window to `Rows' * `Columns'
3852 */
3853 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003854mch_set_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855{
3856 COORD coordScreen;
3857
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003858# ifdef VIMDLL
3859 if (gui.in_use)
3860 return;
3861# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01003862 // Don't change window size while still starting up
Bram Moolenaar071d4272004-06-13 20:20:40 +00003863 if (suppress_winsize != 0)
3864 {
3865 suppress_winsize = 2;
3866 return;
3867 }
3868
3869 if (term_console)
3870 {
3871 coordScreen = GetLargestConsoleWindowSize(g_hConOut);
3872
Bram Moolenaar0f873732019-12-05 20:28:46 +01003873 // Clamp Rows and Columns to reasonable values
Bram Moolenaar071d4272004-06-13 20:20:40 +00003874 if (Rows > coordScreen.Y)
3875 Rows = coordScreen.Y;
3876 if (Columns > coordScreen.X)
3877 Columns = coordScreen.X;
3878
3879 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
3880 }
3881}
3882
3883/*
3884 * Rows and/or Columns has changed.
3885 */
3886 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003887mch_new_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003888{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003889# ifdef VIMDLL
3890 if (gui.in_use)
3891 return;
3892# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893 set_scroll_region(0, 0, Columns - 1, Rows - 1);
3894}
3895
3896
3897/*
3898 * Called when started up, to set the winsize that was delayed.
3899 */
3900 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003901mch_set_winsize_now(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902{
3903 if (suppress_winsize == 2)
3904 {
3905 suppress_winsize = 0;
3906 mch_set_shellsize();
3907 shell_resized();
3908 }
3909 suppress_winsize = 0;
3910}
Bram Moolenaar0f873732019-12-05 20:28:46 +01003911#endif // FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00003912
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003913 static BOOL
3914vim_create_process(
Bram Moolenaar36c85b22013-12-12 20:25:44 +01003915 char *cmd,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003916 BOOL inherit_handles,
Bram Moolenaar3f1138e2014-01-05 13:29:26 +01003917 DWORD flags,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003918 STARTUPINFO *si,
Bram Moolenaar05aafed2017-08-11 19:12:11 +02003919 PROCESS_INFORMATION *pi,
3920 LPVOID *env,
3921 char *cwd)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003922{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003923 BOOL ret = FALSE;
3924 WCHAR *wcmd, *wcwd = NULL;
3925
3926 wcmd = enc_to_utf16((char_u *)cmd, NULL);
3927 if (wcmd == NULL)
3928 return FALSE;
3929 if (cwd != NULL)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003930 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003931 wcwd = enc_to_utf16((char_u *)cwd, NULL);
3932 if (wcwd == NULL)
3933 goto theend;
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003934 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003935
3936 ret = CreateProcessW(
3937 NULL, // Executable name
3938 wcmd, // Command to execute
3939 NULL, // Process security attributes
3940 NULL, // Thread security attributes
3941 inherit_handles, // Inherit handles
3942 flags, // Creation flags
3943 env, // Environment
3944 wcwd, // Current directory
3945 (LPSTARTUPINFOW)si, // Startup information
3946 pi); // Process information
3947theend:
3948 vim_free(wcmd);
3949 vim_free(wcwd);
3950 return ret;
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003951}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952
3953
Bram Moolenaarb2964f22017-03-21 19:29:26 +01003954 static HINSTANCE
3955vim_shell_execute(
3956 char *cmd,
3957 INT n_show_cmd)
3958{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003959 HINSTANCE ret;
3960 WCHAR *wcmd;
3961
3962 wcmd = enc_to_utf16((char_u *)cmd, NULL);
3963 if (wcmd == NULL)
3964 return (HINSTANCE) 0;
3965
3966 ret = ShellExecuteW(NULL, NULL, wcmd, NULL, NULL, n_show_cmd);
3967 vim_free(wcmd);
3968 return ret;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01003969}
3970
3971
Bram Moolenaar4f974752019-02-17 17:44:42 +01003972#if defined(FEAT_GUI_MSWIN) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003973
3974/*
3975 * Specialised version of system() for Win32 GUI mode.
3976 * This version proceeds as follows:
3977 * 1. Create a console window for use by the subprocess
3978 * 2. Run the subprocess (it gets the allocated console by default)
3979 * 3. Wait for the subprocess to terminate and get its exit code
3980 * 4. Prompt the user to press a key to close the console window
3981 */
3982 static int
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003983mch_system_classic(char *cmd, int options)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984{
3985 STARTUPINFO si;
3986 PROCESS_INFORMATION pi;
3987 DWORD ret = 0;
3988 HWND hwnd = GetFocus();
3989
3990 si.cb = sizeof(si);
3991 si.lpReserved = NULL;
3992 si.lpDesktop = NULL;
3993 si.lpTitle = NULL;
3994 si.dwFlags = STARTF_USESHOWWINDOW;
3995 /*
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003996 * It's nicer to run a filter command in a minimized window.
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01003997 * Don't activate the window to keep focus on Vim.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003998 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003999 if (options & SHELL_DOOUT)
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004000 si.wShowWindow = SW_SHOWMINNOACTIVE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004001 else
4002 si.wShowWindow = SW_SHOWNORMAL;
4003 si.cbReserved2 = 0;
4004 si.lpReserved2 = NULL;
4005
Bram Moolenaar0f873732019-12-05 20:28:46 +01004006 // Now, run the command
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004007 vim_create_process(cmd, FALSE,
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004008 CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE,
4009 &si, &pi, NULL, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004010
Bram Moolenaar0f873732019-12-05 20:28:46 +01004011 // Wait for the command to terminate before continuing
Bram Moolenaar071d4272004-06-13 20:20:40 +00004012 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004013# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00004014 int delay = 1;
4015
Bram Moolenaar0f873732019-12-05 20:28:46 +01004016 // Keep updating the window while waiting for the shell to finish.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004017 for (;;)
4018 {
4019 MSG msg;
4020
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02004021 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004022 {
4023 TranslateMessage(&msg);
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02004024 pDispatchMessage(&msg);
Bram Moolenaare4195c52012-08-02 12:31:44 +02004025 delay = 1;
4026 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004027 }
4028 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4029 break;
4030
Bram Moolenaar0f873732019-12-05 20:28:46 +01004031 // We start waiting for a very short time and then increase it, so
4032 // that we respond quickly when the process is quick, and don't
4033 // consume too much overhead when it's slow.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004034 if (delay < 50)
4035 delay += 10;
4036 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004037# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004038 WaitForSingleObject(pi.hProcess, INFINITE);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004039# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040
Bram Moolenaar0f873732019-12-05 20:28:46 +01004041 // Get the command exit code
Bram Moolenaar071d4272004-06-13 20:20:40 +00004042 GetExitCodeProcess(pi.hProcess, &ret);
4043 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044
Bram Moolenaar0f873732019-12-05 20:28:46 +01004045 // Close the handles to the subprocess, so that it goes away
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046 CloseHandle(pi.hThread);
4047 CloseHandle(pi.hProcess);
4048
Bram Moolenaar0f873732019-12-05 20:28:46 +01004049 // Try to get input focus back. Doesn't always work though.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004050 PostMessage(hwnd, WM_SETFOCUS, 0, 0);
4051
4052 return ret;
4053}
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004054
4055/*
4056 * Thread launched by the gui to send the current buffer data to the
4057 * process. This way avoid to hang up vim totally if the children
4058 * process take a long time to process the lines.
4059 */
Bram Moolenaar4c38d662016-08-03 20:54:57 +02004060 static unsigned int __stdcall
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004061sub_process_writer(LPVOID param)
4062{
4063 HANDLE g_hChildStd_IN_Wr = param;
4064 linenr_T lnum = curbuf->b_op_start.lnum;
4065 DWORD len = 0;
4066 DWORD l;
4067 char_u *lp = ml_get(lnum);
4068 char_u *s;
4069 int written = 0;
4070
4071 for (;;)
4072 {
4073 l = (DWORD)STRLEN(lp + written);
4074 if (l == 0)
4075 len = 0;
4076 else if (lp[written] == NL)
4077 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004078 // NL -> NUL translation
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004079 WriteFile(g_hChildStd_IN_Wr, "", 1, &len, NULL);
4080 }
4081 else
4082 {
4083 s = vim_strchr(lp + written, NL);
4084 WriteFile(g_hChildStd_IN_Wr, (char *)lp + written,
4085 s == NULL ? l : (DWORD)(s - (lp + written)),
4086 &len, NULL);
4087 }
4088 if (len == (int)l)
4089 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004090 // Finished a line, add a NL, unless this line should not have
4091 // one.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004092 if (lnum != curbuf->b_op_end.lnum
Bram Moolenaar34d72d42015-07-17 14:18:08 +02004093 || (!curbuf->b_p_bin
4094 && curbuf->b_p_fixeol)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004095 || (lnum != curbuf->b_no_eol_lnum
4096 && (lnum != curbuf->b_ml.ml_line_count
4097 || curbuf->b_p_eol)))
4098 {
Bram Moolenaar42335f52018-09-13 15:33:43 +02004099 WriteFile(g_hChildStd_IN_Wr, "\n", 1,
4100 (LPDWORD)&vim_ignored, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004101 }
4102
4103 ++lnum;
4104 if (lnum > curbuf->b_op_end.lnum)
4105 break;
4106
4107 lp = ml_get(lnum);
4108 written = 0;
4109 }
4110 else if (len > 0)
4111 written += len;
4112 }
4113
Bram Moolenaar0f873732019-12-05 20:28:46 +01004114 // finished all the lines, close pipe
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004115 CloseHandle(g_hChildStd_IN_Wr);
Bram Moolenaar86f2cd52016-08-02 21:55:17 +02004116 return 0;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004117}
4118
4119
Bram Moolenaar0f873732019-12-05 20:28:46 +01004120# define BUFLEN 100 // length for buffer, stolen from unix version
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004121
4122/*
4123 * This function read from the children's stdout and write the
4124 * data on screen or in the buffer accordingly.
4125 */
4126 static void
4127dump_pipe(int options,
4128 HANDLE g_hChildStd_OUT_Rd,
4129 garray_T *ga,
4130 char_u buffer[],
4131 DWORD *buffer_off)
4132{
4133 DWORD availableBytes = 0;
4134 DWORD i;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004135 int ret;
4136 DWORD len;
4137 DWORD toRead;
4138 int repeatCount;
4139
Bram Moolenaar0f873732019-12-05 20:28:46 +01004140 // we query the pipe to see if there is any data to read
4141 // to avoid to perform a blocking read
4142 ret = PeekNamedPipe(g_hChildStd_OUT_Rd, // pipe to query
4143 NULL, // optional buffer
4144 0, // buffer size
4145 NULL, // number of read bytes
4146 &availableBytes, // available bytes total
4147 NULL); // byteLeft
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004148
4149 repeatCount = 0;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004150 // We got real data in the pipe, read it
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02004151 while (ret != 0 && availableBytes > 0)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004152 {
4153 repeatCount++;
Bram Moolenaara12a1612019-01-24 16:39:02 +01004154 toRead = (DWORD)(BUFLEN - *buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004155 toRead = availableBytes < toRead ? availableBytes : toRead;
Bram Moolenaara12a1612019-01-24 16:39:02 +01004156 ReadFile(g_hChildStd_OUT_Rd, buffer + *buffer_off, toRead , &len, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004157
Bram Moolenaar0f873732019-12-05 20:28:46 +01004158 // If we haven't read anything, there is a problem
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004159 if (len == 0)
4160 break;
4161
4162 availableBytes -= len;
4163
4164 if (options & SHELL_READ)
4165 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004166 // Do NUL -> NL translation, append NL separated
4167 // lines to the current buffer.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004168 for (i = 0; i < len; ++i)
4169 {
4170 if (buffer[i] == NL)
4171 append_ga_line(ga);
4172 else if (buffer[i] == NUL)
4173 ga_append(ga, NL);
4174 else
4175 ga_append(ga, buffer[i]);
4176 }
4177 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004178 else if (has_mbyte)
4179 {
4180 int l;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004181 int c;
4182 char_u *p;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004183
4184 len += *buffer_off;
4185 buffer[len] = NUL;
4186
Bram Moolenaar0f873732019-12-05 20:28:46 +01004187 // Check if the last character in buffer[] is
4188 // incomplete, keep these bytes for the next
4189 // round.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004190 for (p = buffer; p < buffer + len; p += l)
4191 {
Bram Moolenaard3c907b2016-08-17 21:32:09 +02004192 l = MB_CPTR2LEN(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004193 if (l == 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004194 l = 1; // NUL byte?
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004195 else if (MB_BYTE2LEN(*p) != l)
4196 break;
4197 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004198 if (p == buffer) // no complete character
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004199 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004200 // avoid getting stuck at an illegal byte
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004201 if (len >= 12)
4202 ++p;
4203 else
4204 {
4205 *buffer_off = len;
4206 return;
4207 }
4208 }
4209 c = *p;
4210 *p = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004211 msg_puts((char *)buffer);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004212 if (p < buffer + len)
4213 {
4214 *p = c;
4215 *buffer_off = (DWORD)((buffer + len) - p);
4216 mch_memmove(buffer, p, *buffer_off);
4217 return;
4218 }
4219 *buffer_off = 0;
4220 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004221 else
4222 {
4223 buffer[len] = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004224 msg_puts((char *)buffer);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004225 }
4226
4227 windgoto(msg_row, msg_col);
4228 cursor_on();
4229 out_flush();
4230 }
4231}
4232
4233/*
4234 * Version of system to use for windows NT > 5.0 (Win2K), use pipe
4235 * for communication and doesn't open any new window.
4236 */
4237 static int
4238mch_system_piped(char *cmd, int options)
4239{
4240 STARTUPINFO si;
4241 PROCESS_INFORMATION pi;
4242 DWORD ret = 0;
4243
4244 HANDLE g_hChildStd_IN_Rd = NULL;
4245 HANDLE g_hChildStd_IN_Wr = NULL;
4246 HANDLE g_hChildStd_OUT_Rd = NULL;
4247 HANDLE g_hChildStd_OUT_Wr = NULL;
4248
Bram Moolenaar0f873732019-12-05 20:28:46 +01004249 char_u buffer[BUFLEN + 1]; // reading buffer + size
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004250 DWORD len;
4251
Bram Moolenaar0f873732019-12-05 20:28:46 +01004252 // buffer used to receive keys
4253 char_u ta_buf[BUFLEN + 1]; // TypeAHead
4254 int ta_len = 0; // valid bytes in ta_buf[]
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004255
4256 DWORD i;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004257 int noread_cnt = 0;
4258 garray_T ga;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004259 int delay = 1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004260 DWORD buffer_off = 0; // valid bytes in buffer[]
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004261 char *p = NULL;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004262
4263 SECURITY_ATTRIBUTES saAttr;
4264
Bram Moolenaar0f873732019-12-05 20:28:46 +01004265 // Set the bInheritHandle flag so pipe handles are inherited.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004266 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
4267 saAttr.bInheritHandle = TRUE;
4268 saAttr.lpSecurityDescriptor = NULL;
4269
4270 if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004271 // Ensure the read handle to the pipe for STDOUT is not inherited.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004272 || ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004273 // Create a pipe for the child process's STDIN.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004274 || ! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004275 // Ensure the write handle to the pipe for STDIN is not inherited.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004276 || ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004277 {
4278 CloseHandle(g_hChildStd_IN_Rd);
4279 CloseHandle(g_hChildStd_IN_Wr);
4280 CloseHandle(g_hChildStd_OUT_Rd);
4281 CloseHandle(g_hChildStd_OUT_Wr);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004282 msg_puts(_("\nCannot create pipes\n"));
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004283 }
4284
4285 si.cb = sizeof(si);
4286 si.lpReserved = NULL;
4287 si.lpDesktop = NULL;
4288 si.lpTitle = NULL;
4289 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
4290
Bram Moolenaar0f873732019-12-05 20:28:46 +01004291 // set-up our file redirection
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004292 si.hStdError = g_hChildStd_OUT_Wr;
4293 si.hStdOutput = g_hChildStd_OUT_Wr;
4294 si.hStdInput = g_hChildStd_IN_Rd;
4295 si.wShowWindow = SW_HIDE;
4296 si.cbReserved2 = 0;
4297 si.lpReserved2 = NULL;
4298
4299 if (options & SHELL_READ)
4300 ga_init2(&ga, 1, BUFLEN);
4301
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004302 if (cmd != NULL)
4303 {
4304 p = (char *)vim_strsave((char_u *)cmd);
4305 if (p != NULL)
4306 unescape_shellxquote((char_u *)p, p_sxe);
4307 else
4308 p = cmd;
4309 }
4310
Bram Moolenaar0f873732019-12-05 20:28:46 +01004311 // Now, run the command.
4312 // About "Inherit handles" being TRUE: this command can be litigious,
4313 // handle inheritance was deactivated for pending temp file, but, if we
4314 // deactivate it, the pipes don't work for some reason.
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004315 vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE,
4316 &si, &pi, NULL, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004317
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004318 if (p != cmd)
4319 vim_free(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004320
Bram Moolenaar0f873732019-12-05 20:28:46 +01004321 // Close our unused side of the pipes
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004322 CloseHandle(g_hChildStd_IN_Rd);
4323 CloseHandle(g_hChildStd_OUT_Wr);
4324
4325 if (options & SHELL_WRITE)
4326 {
Bram Moolenaar86f2cd52016-08-02 21:55:17 +02004327 HANDLE thread = (HANDLE)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004328 _beginthreadex(NULL, // security attributes
4329 0, // default stack size
4330 sub_process_writer, // function to be executed
4331 g_hChildStd_IN_Wr, // parameter
4332 0, // creation flag, start immediately
4333 NULL); // we don't care about thread id
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004334 CloseHandle(thread);
4335 g_hChildStd_IN_Wr = NULL;
4336 }
4337
Bram Moolenaar0f873732019-12-05 20:28:46 +01004338 // Keep updating the window while waiting for the shell to finish.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004339 for (;;)
4340 {
4341 MSG msg;
4342
Bram Moolenaar175d0702013-12-11 18:36:33 +01004343 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004344 {
4345 TranslateMessage(&msg);
Bram Moolenaar175d0702013-12-11 18:36:33 +01004346 pDispatchMessage(&msg);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004347 }
4348
Bram Moolenaar0f873732019-12-05 20:28:46 +01004349 // write pipe information in the window
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004350 if ((options & (SHELL_READ|SHELL_WRITE))
4351# ifdef FEAT_GUI
4352 || gui.in_use
4353# endif
4354 )
4355 {
4356 len = 0;
4357 if (!(options & SHELL_EXPAND)
4358 && ((options &
4359 (SHELL_READ|SHELL_WRITE|SHELL_COOKED))
4360 != (SHELL_READ|SHELL_WRITE|SHELL_COOKED)
4361# ifdef FEAT_GUI
4362 || gui.in_use
4363# endif
4364 )
4365 && (ta_len > 0 || noread_cnt > 4))
4366 {
4367 if (ta_len == 0)
4368 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004369 // Get extra characters when we don't have any. Reset the
4370 // counter and timer.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004371 noread_cnt = 0;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004372 len = ui_inchar(ta_buf, BUFLEN, 10L, 0);
4373 }
4374 if (ta_len > 0 || len > 0)
4375 {
4376 /*
4377 * For pipes: Check for CTRL-C: send interrupt signal to
4378 * child. Check for CTRL-D: EOF, close pipe to child.
4379 */
4380 if (len == 1 && cmd != NULL)
4381 {
4382 if (ta_buf[ta_len] == Ctrl_C)
4383 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004384 // Learn what exit code is expected, for
4385 // now put 9 as SIGKILL
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004386 TerminateProcess(pi.hProcess, 9);
4387 }
4388 if (ta_buf[ta_len] == Ctrl_D)
4389 {
4390 CloseHandle(g_hChildStd_IN_Wr);
4391 g_hChildStd_IN_Wr = NULL;
4392 }
4393 }
4394
Bram Moolenaarf4140482020-02-15 23:06:45 +01004395 term_replace_bs_del_keycode(ta_buf, ta_len, len);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004396
4397 /*
4398 * For pipes: echo the typed characters. For a pty this
4399 * does not seem to work.
4400 */
4401 for (i = ta_len; i < ta_len + len; ++i)
4402 {
4403 if (ta_buf[i] == '\n' || ta_buf[i] == '\b')
4404 msg_putchar(ta_buf[i]);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004405 else if (has_mbyte)
4406 {
4407 int l = (*mb_ptr2len)(ta_buf + i);
4408
4409 msg_outtrans_len(ta_buf + i, l);
4410 i += l - 1;
4411 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004412 else
4413 msg_outtrans_len(ta_buf + i, 1);
4414 }
4415 windgoto(msg_row, msg_col);
4416 out_flush();
4417
4418 ta_len += len;
4419
4420 /*
4421 * Write the characters to the child, unless EOF has been
4422 * typed for pipes. Write one character at a time, to
4423 * avoid losing too much typeahead. When writing buffer
4424 * lines, drop the typed characters (only check for
4425 * CTRL-C).
4426 */
4427 if (options & SHELL_WRITE)
4428 ta_len = 0;
4429 else if (g_hChildStd_IN_Wr != NULL)
4430 {
4431 WriteFile(g_hChildStd_IN_Wr, (char*)ta_buf,
4432 1, &len, NULL);
4433 // if we are typing in, we want to keep things reactive
4434 delay = 1;
4435 if (len > 0)
4436 {
4437 ta_len -= len;
4438 mch_memmove(ta_buf, ta_buf + len, ta_len);
4439 }
4440 }
4441 }
4442 }
4443 }
4444
4445 if (ta_len)
4446 ui_inchar_undo(ta_buf, ta_len);
4447
4448 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4449 {
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004450 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004451 break;
4452 }
4453
4454 ++noread_cnt;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004455 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004456
Bram Moolenaar0f873732019-12-05 20:28:46 +01004457 // We start waiting for a very short time and then increase it, so
4458 // that we respond quickly when the process is quick, and don't
4459 // consume too much overhead when it's slow.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004460 if (delay < 50)
4461 delay += 10;
4462 }
4463
Bram Moolenaar0f873732019-12-05 20:28:46 +01004464 // Close the pipe
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004465 CloseHandle(g_hChildStd_OUT_Rd);
4466 if (g_hChildStd_IN_Wr != NULL)
4467 CloseHandle(g_hChildStd_IN_Wr);
4468
4469 WaitForSingleObject(pi.hProcess, INFINITE);
4470
Bram Moolenaar0f873732019-12-05 20:28:46 +01004471 // Get the command exit code
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004472 GetExitCodeProcess(pi.hProcess, &ret);
4473
4474 if (options & SHELL_READ)
4475 {
4476 if (ga.ga_len > 0)
4477 {
4478 append_ga_line(&ga);
Bram Moolenaar0f873732019-12-05 20:28:46 +01004479 // remember that the NL was missing
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004480 curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
4481 }
4482 else
4483 curbuf->b_no_eol_lnum = 0;
4484 ga_clear(&ga);
4485 }
4486
Bram Moolenaar0f873732019-12-05 20:28:46 +01004487 // Close the handles to the subprocess, so that it goes away
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004488 CloseHandle(pi.hThread);
4489 CloseHandle(pi.hProcess);
4490
4491 return ret;
4492}
4493
4494 static int
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004495mch_system_g(char *cmd, int options)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004496{
Bram Moolenaar0f873732019-12-05 20:28:46 +01004497 // if we can pipe and the shelltemp option is off
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004498 if (!p_stmp)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004499 return mch_system_piped(cmd, options);
4500 else
4501 return mch_system_classic(cmd, options);
4502}
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004503#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004505#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004506 static int
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02004507mch_system_c(char *cmd, int options UNUSED)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004508{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004509 int ret;
4510 WCHAR *wcmd;
Bram Moolenaar2efc44b2019-10-05 12:09:32 +02004511 char_u *buf;
4512 size_t len;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004513
Bram Moolenaar2efc44b2019-10-05 12:09:32 +02004514 // If the command starts and ends with double quotes, enclose the command
4515 // in parentheses.
4516 len = STRLEN(cmd);
4517 if (len >= 2 && cmd[0] == '"' && cmd[len - 1] == '"')
4518 {
4519 len += 3;
4520 buf = alloc(len);
4521 if (buf == NULL)
4522 return -1;
4523 vim_snprintf((char *)buf, len, "(%s)", cmd);
4524 wcmd = enc_to_utf16(buf, NULL);
4525 free(buf);
4526 }
4527 else
4528 wcmd = enc_to_utf16((char_u *)cmd, NULL);
4529
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004530 if (wcmd == NULL)
4531 return -1;
4532
4533 ret = _wsystem(wcmd);
4534 vim_free(wcmd);
4535 return ret;
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004536}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004537
4538#endif
4539
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004540 static int
4541mch_system(char *cmd, int options)
4542{
4543#ifdef VIMDLL
Bram Moolenaar294d9bf2019-05-23 20:12:46 +02004544 if (gui.in_use || gui.starting)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004545 return mch_system_g(cmd, options);
4546 else
4547 return mch_system_c(cmd, options);
4548#elif defined(FEAT_GUI_MSWIN)
4549 return mch_system_g(cmd, options);
4550#else
4551 return mch_system_c(cmd, options);
4552#endif
4553}
4554
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004555#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
4556/*
4557 * Use a terminal window to run a shell command in.
4558 */
4559 static int
4560mch_call_shell_terminal(
4561 char_u *cmd,
Bram Moolenaar0f873732019-12-05 20:28:46 +01004562 int options UNUSED) // SHELL_*, see vim.h
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004563{
4564 jobopt_T opt;
4565 char_u *newcmd = NULL;
4566 typval_T argvar[2];
4567 long_u cmdlen;
4568 int retval = -1;
4569 buf_T *buf;
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004570 job_T *job;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004571 aco_save_T aco;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004572 oparg_T oa; // operator arguments
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004573
Bram Moolenaar42f652f2018-03-19 21:44:37 +01004574 if (cmd == NULL)
4575 cmdlen = STRLEN(p_sh) + 1;
4576 else
4577 cmdlen = STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10;
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02004578 newcmd = alloc(cmdlen);
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004579 if (newcmd == NULL)
4580 return 255;
Bram Moolenaar42f652f2018-03-19 21:44:37 +01004581 if (cmd == NULL)
4582 {
4583 STRCPY(newcmd, p_sh);
4584 ch_log(NULL, "starting terminal to run a shell");
4585 }
4586 else
4587 {
4588 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s", p_sh, p_shcf, cmd);
4589 ch_log(NULL, "starting terminal for system command '%s'", cmd);
4590 }
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004591
4592 init_job_options(&opt);
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004593
4594 argvar[0].v_type = VAR_STRING;
4595 argvar[0].vval.v_string = newcmd;
4596 argvar[1].v_type = VAR_UNKNOWN;
4597 buf = term_start(argvar, NULL, &opt, TERM_START_SYSTEM);
Bram Moolenaar81c3c89a2018-03-20 11:41:44 +01004598 if (buf == NULL)
Bram Moolenaar9029b912019-03-21 19:58:00 +01004599 {
4600 vim_free(newcmd);
Bram Moolenaar81c3c89a2018-03-20 11:41:44 +01004601 return 255;
Bram Moolenaar9029b912019-03-21 19:58:00 +01004602 }
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004603
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004604 job = term_getjob(buf->b_term);
4605 ++job->jv_refcount;
4606
Bram Moolenaar0f873732019-12-05 20:28:46 +01004607 // Find a window to make "buf" curbuf.
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004608 aucmd_prepbuf(&aco, buf);
4609
4610 clear_oparg(&oa);
4611 while (term_use_loop())
4612 {
4613 if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
4614 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004615 // If terminal_loop() returns OK we got a key that is handled
4616 // in Normal model. We don't do redrawing anyway.
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004617 if (terminal_loop(TRUE) == OK)
4618 normal_cmd(&oa, TRUE);
4619 }
4620 else
4621 normal_cmd(&oa, TRUE);
4622 }
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004623 retval = job->jv_exitval;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004624 ch_log(NULL, "system command finished");
4625
Bram Moolenaarf9c38832018-06-19 19:59:20 +02004626 job_unref(job);
4627
Bram Moolenaar0f873732019-12-05 20:28:46 +01004628 // restore curwin/curbuf and a few other things
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004629 aucmd_restbuf(&aco);
4630
4631 wait_return(TRUE);
4632 do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
4633
4634 vim_free(newcmd);
4635 return retval;
4636}
4637#endif
4638
Bram Moolenaar071d4272004-06-13 20:20:40 +00004639/*
4640 * Either execute a command by calling the shell or start a new shell
4641 */
4642 int
4643mch_call_shell(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004644 char_u *cmd,
Bram Moolenaar0f873732019-12-05 20:28:46 +01004645 int options) // SHELL_*, see vim.h
Bram Moolenaar071d4272004-06-13 20:20:40 +00004646{
4647 int x = 0;
4648 int tmode = cur_tmode;
4649#ifdef FEAT_TITLE
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004650 WCHAR szShellTitle[512];
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004651
Bram Moolenaar0f873732019-12-05 20:28:46 +01004652 // Change the title to reflect that we are in a subshell.
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004653 if (GetConsoleTitleW(szShellTitle,
4654 sizeof(szShellTitle)/sizeof(WCHAR) - 4) > 0)
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004655 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004656 if (cmd == NULL)
4657 wcscat(szShellTitle, L" :sh");
4658 else
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004659 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004660 WCHAR *wn = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004661
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004662 if (wn != NULL)
4663 {
4664 wcscat(szShellTitle, L" - !");
4665 if ((wcslen(szShellTitle) + wcslen(wn) <
4666 sizeof(szShellTitle)/sizeof(WCHAR)))
4667 wcscat(szShellTitle, wn);
4668 SetConsoleTitleW(szShellTitle);
4669 vim_free(wn);
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004670 }
4671 }
4672 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004673#endif
4674
4675 out_flush();
4676
4677#ifdef MCH_WRITE_DUMP
4678 if (fdDump)
4679 {
4680 fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
4681 fflush(fdDump);
4682 }
4683#endif
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004684#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02004685 // TODO: make the terminal window work with input or output redirected.
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004686 if (
4687# ifdef VIMDLL
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02004688 gui.in_use &&
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004689# endif
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02004690 vim_strchr(p_go, GO_TERMINAL) != NULL
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004691 && (options & (SHELL_FILTER|SHELL_DOOUT|SHELL_WRITE|SHELL_READ)) == 0)
4692 {
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02004693 char_u *cmdbase = cmd;
4694
Bram Moolenaar4d5c1262019-09-20 17:20:02 +02004695 if (cmdbase != NULL)
4696 // Skip a leading quote and (.
4697 while (*cmdbase == '"' || *cmdbase == '(')
4698 ++cmdbase;
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02004699
4700 // Check the command does not begin with "start "
Bram Moolenaar36e7a822019-11-13 21:49:24 +01004701 if (cmdbase == NULL || STRNICMP(cmdbase, "start", 5) != 0
4702 || !VIM_ISWHITE(cmdbase[5]))
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02004703 {
4704 // Use a terminal window to run the command in.
4705 x = mch_call_shell_terminal(cmd, options);
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01004706# ifdef FEAT_TITLE
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02004707 resettitle();
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01004708# endif
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02004709 return x;
4710 }
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004711 }
4712#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004713
4714 /*
4715 * Catch all deadly signals while running the external command, because a
4716 * CTRL-C, Ctrl-Break or illegal instruction might otherwise kill us.
4717 */
4718 signal(SIGINT, SIG_IGN);
4719#if defined(__GNUC__) && !defined(__MINGW32__)
4720 signal(SIGKILL, SIG_IGN);
4721#else
4722 signal(SIGBREAK, SIG_IGN);
4723#endif
4724 signal(SIGILL, SIG_IGN);
4725 signal(SIGFPE, SIG_IGN);
4726 signal(SIGSEGV, SIG_IGN);
4727 signal(SIGTERM, SIG_IGN);
4728 signal(SIGABRT, SIG_IGN);
4729
4730 if (options & SHELL_COOKED)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004731 settmode(TMODE_COOK); // set to normal mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00004732
4733 if (cmd == NULL)
4734 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004735 x = mch_system((char *)p_sh, options);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004736 }
4737 else
4738 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004739 // we use "command" or "cmd" to start the shell; slow but easy
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004740 char_u *newcmd = NULL;
4741 char_u *cmdbase = cmd;
4742 long_u cmdlen;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004743
Bram Moolenaar0f873732019-12-05 20:28:46 +01004744 // Skip a leading ", ( and "(.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004745 if (*cmdbase == '"' )
4746 ++cmdbase;
4747 if (*cmdbase == '(')
4748 ++cmdbase;
4749
Bram Moolenaar1c465442017-03-12 20:10:05 +01004750 if ((STRNICMP(cmdbase, "start", 5) == 0) && VIM_ISWHITE(cmdbase[5]))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004751 {
4752 STARTUPINFO si;
4753 PROCESS_INFORMATION pi;
4754 DWORD flags = CREATE_NEW_CONSOLE;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01004755 INT n_show_cmd = SW_SHOWNORMAL;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004756 char_u *p;
4757
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01004758 ZeroMemory(&si, sizeof(si));
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004759 si.cb = sizeof(si);
4760 si.lpReserved = NULL;
4761 si.lpDesktop = NULL;
4762 si.lpTitle = NULL;
4763 si.dwFlags = 0;
4764 si.cbReserved2 = 0;
4765 si.lpReserved2 = NULL;
4766
4767 cmdbase = skipwhite(cmdbase + 5);
4768 if ((STRNICMP(cmdbase, "/min", 4) == 0)
Bram Moolenaar1c465442017-03-12 20:10:05 +01004769 && VIM_ISWHITE(cmdbase[4]))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004770 {
4771 cmdbase = skipwhite(cmdbase + 4);
4772 si.dwFlags = STARTF_USESHOWWINDOW;
4773 si.wShowWindow = SW_SHOWMINNOACTIVE;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01004774 n_show_cmd = SW_SHOWMINNOACTIVE;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004775 }
4776 else if ((STRNICMP(cmdbase, "/b", 2) == 0)
Bram Moolenaar1c465442017-03-12 20:10:05 +01004777 && VIM_ISWHITE(cmdbase[2]))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004778 {
4779 cmdbase = skipwhite(cmdbase + 2);
4780 flags = CREATE_NO_WINDOW;
4781 si.dwFlags = STARTF_USESTDHANDLES;
4782 si.hStdInput = CreateFile("\\\\.\\NUL", // File name
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004783 GENERIC_READ, // Access flags
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004784 0, // Share flags
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004785 NULL, // Security att.
4786 OPEN_EXISTING, // Open flags
4787 FILE_ATTRIBUTE_NORMAL, // File att.
4788 NULL); // Temp file
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004789 si.hStdOutput = si.hStdInput;
4790 si.hStdError = si.hStdInput;
4791 }
4792
Bram Moolenaar0f873732019-12-05 20:28:46 +01004793 // Remove a trailing ", ) and )" if they have a match
4794 // at the start of the command.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004795 if (cmdbase > cmd)
4796 {
4797 p = cmdbase + STRLEN(cmdbase);
4798 if (p > cmdbase && p[-1] == '"' && *cmd == '"')
4799 *--p = NUL;
4800 if (p > cmdbase && p[-1] == ')'
4801 && (*cmd =='(' || cmd[1] == '('))
4802 *--p = NUL;
4803 }
4804
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004805 newcmd = cmdbase;
4806 unescape_shellxquote(cmdbase, p_sxe);
4807
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004808 /*
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004809 * If creating new console, arguments are passed to the
4810 * 'cmd.exe' as-is. If it's not, arguments are not treated
4811 * correctly for current 'cmd.exe'. So unescape characters in
4812 * shellxescape except '|' for avoiding to be treated as
4813 * argument to them. Pass the arguments to sub-shell.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004814 */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004815 if (flags != CREATE_NEW_CONSOLE)
4816 {
4817 char_u *subcmd;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004818 char_u *cmd_shell = mch_getenv("COMSPEC");
4819
4820 if (cmd_shell == NULL || *cmd_shell == NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004821 cmd_shell = (char_u *)default_shell();
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004822
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004823 subcmd = vim_strsave_escaped_ext(cmdbase,
4824 (char_u *)"|", '^', FALSE);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004825 if (subcmd != NULL)
4826 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004827 // make "cmd.exe /c arguments"
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004828 cmdlen = STRLEN(cmd_shell) + STRLEN(subcmd) + 5;
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02004829 newcmd = alloc(cmdlen);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004830 if (newcmd != NULL)
4831 vim_snprintf((char *)newcmd, cmdlen, "%s /c %s",
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004832 cmd_shell, subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004833 else
4834 newcmd = cmdbase;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004835 vim_free(subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004836 }
4837 }
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004838
4839 /*
4840 * Now, start the command as a process, so that it doesn't
4841 * inherit our handles which causes unpleasant dangling swap
4842 * files if we exit before the spawned process
4843 */
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004844 if (vim_create_process((char *)newcmd, FALSE, flags,
4845 &si, &pi, NULL, NULL))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004846 x = 0;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01004847 else if (vim_shell_execute((char *)newcmd, n_show_cmd)
4848 > (HINSTANCE)32)
4849 x = 0;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004850 else
4851 {
4852 x = -1;
Bram Moolenaar4f974752019-02-17 17:44:42 +01004853#ifdef FEAT_GUI_MSWIN
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004854# ifdef VIMDLL
4855 if (gui.in_use)
4856# endif
4857 emsg(_("E371: Command not found"));
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004858#endif
4859 }
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004860
4861 if (newcmd != cmdbase)
4862 vim_free(newcmd);
4863
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01004864 if (si.dwFlags == STARTF_USESTDHANDLES && si.hStdInput != NULL)
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004865 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004866 // Close the handle to \\.\NUL created above.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004867 CloseHandle(si.hStdInput);
4868 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004869 // Close the handles to the subprocess, so that it goes away
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004870 CloseHandle(pi.hThread);
4871 CloseHandle(pi.hProcess);
4872 }
4873 else
4874 {
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02004875 cmdlen =
Bram Moolenaar4f974752019-02-17 17:44:42 +01004876#ifdef FEAT_GUI_MSWIN
Bram Moolenaar294d9bf2019-05-23 20:12:46 +02004877 ((gui.in_use || gui.starting) ?
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02004878 (!s_dont_use_vimrun && p_stmp ?
4879 STRLEN(vimrun_path) : STRLEN(p_sh) + STRLEN(p_shcf))
4880 : 0) +
Bram Moolenaar071d4272004-06-13 20:20:40 +00004881#endif
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02004882 STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10;
Bram Moolenaar0fde2902008-03-16 13:54:13 +00004883
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02004884 newcmd = alloc(cmdlen);
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004885 if (newcmd != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004886 {
Bram Moolenaar4f974752019-02-17 17:44:42 +01004887#if defined(FEAT_GUI_MSWIN)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004888 if (
4889# ifdef VIMDLL
Bram Moolenaar294d9bf2019-05-23 20:12:46 +02004890 (gui.in_use || gui.starting) &&
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004891# endif
4892 need_vimrun_warning)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004893 {
Bram Moolenaar63e43442016-11-19 17:28:44 +01004894 char *msg = _("VIMRUN.EXE not found in your $PATH.\n"
4895 "External commands will not pause after completion.\n"
4896 "See :help win32-vimrun for more information.");
4897 char *title = _("Vim Warning");
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004898 WCHAR *wmsg = enc_to_utf16((char_u *)msg, NULL);
4899 WCHAR *wtitle = enc_to_utf16((char_u *)title, NULL);
Bram Moolenaar63e43442016-11-19 17:28:44 +01004900
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004901 if (wmsg != NULL && wtitle != NULL)
4902 MessageBoxW(NULL, wmsg, wtitle, MB_ICONWARNING);
4903 vim_free(wmsg);
4904 vim_free(wtitle);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004905 need_vimrun_warning = FALSE;
4906 }
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004907 if (
4908# ifdef VIMDLL
Bram Moolenaar294d9bf2019-05-23 20:12:46 +02004909 (gui.in_use || gui.starting) &&
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004910# endif
4911 !s_dont_use_vimrun && p_stmp)
Bram Moolenaarfcc4d922019-05-24 13:32:36 +02004912 // Use vimrun to execute the command. It opens a console
4913 // window, which can be closed without killing Vim.
Bram Moolenaarcc448b32010-07-14 16:52:17 +02004914 vim_snprintf((char *)newcmd, cmdlen, "%s%s%s %s %s",
Bram Moolenaar071d4272004-06-13 20:20:40 +00004915 vimrun_path,
4916 (msg_silent != 0 || (options & SHELL_DOOUT))
4917 ? "-s " : "",
4918 p_sh, p_shcf, cmd);
Bram Moolenaarfcc4d922019-05-24 13:32:36 +02004919 else if (
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02004920# ifdef VIMDLL
Bram Moolenaarfcc4d922019-05-24 13:32:36 +02004921 (gui.in_use || gui.starting) &&
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02004922# endif
Bram Moolenaar0e6bfb92019-07-31 20:53:56 +02004923 s_dont_use_vimrun && STRCMP(p_shcf, "/c") == 0)
Bram Moolenaarfcc4d922019-05-24 13:32:36 +02004924 // workaround for the case that "vimrun" does not exist
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02004925 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s %s %s",
4926 p_sh, p_shcf, p_sh, p_shcf, cmd);
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02004927 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004928#endif
Bram Moolenaarcc448b32010-07-14 16:52:17 +02004929 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s",
Bram Moolenaar0fde2902008-03-16 13:54:13 +00004930 p_sh, p_shcf, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004931 x = mch_system((char *)newcmd, options);
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004932 vim_free(newcmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004933 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004934 }
4935 }
4936
4937 if (tmode == TMODE_RAW)
Bram Moolenaar3b1f18f2020-05-16 23:15:08 +02004938 {
4939 // The shell may have messed with the mode, always set it.
4940 cur_tmode = TMODE_UNKNOWN;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004941 settmode(TMODE_RAW); // set to raw mode
Bram Moolenaar3b1f18f2020-05-16 23:15:08 +02004942 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004943
Bram Moolenaar0f873732019-12-05 20:28:46 +01004944 // Print the return value, unless "vimrun" was used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004945 if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent
Bram Moolenaar4f974752019-02-17 17:44:42 +01004946#if defined(FEAT_GUI_MSWIN)
Bram Moolenaar294d9bf2019-05-23 20:12:46 +02004947 && ((gui.in_use || gui.starting) ?
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004948 ((options & SHELL_DOOUT) || s_dont_use_vimrun || !p_stmp) : 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004949#endif
4950 )
4951 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004952 smsg(_("shell returned %d"), x);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004953 msg_putchar('\n');
4954 }
4955#ifdef FEAT_TITLE
4956 resettitle();
4957#endif
4958
4959 signal(SIGINT, SIG_DFL);
4960#if defined(__GNUC__) && !defined(__MINGW32__)
4961 signal(SIGKILL, SIG_DFL);
4962#else
4963 signal(SIGBREAK, SIG_DFL);
4964#endif
4965 signal(SIGILL, SIG_DFL);
4966 signal(SIGFPE, SIG_DFL);
4967 signal(SIGSEGV, SIG_DFL);
4968 signal(SIGTERM, SIG_DFL);
4969 signal(SIGABRT, SIG_DFL);
4970
4971 return x;
4972}
4973
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01004974#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01004975 static HANDLE
4976job_io_file_open(
Bram Moolenaar972c3b82017-01-12 21:44:49 +01004977 char_u *fname,
4978 DWORD dwDesiredAccess,
4979 DWORD dwShareMode,
4980 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
4981 DWORD dwCreationDisposition,
4982 DWORD dwFlagsAndAttributes)
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01004983{
4984 HANDLE h;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004985 WCHAR *wn;
Bram Moolenaara12a1612019-01-24 16:39:02 +01004986
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004987 wn = enc_to_utf16(fname, NULL);
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01004988 if (wn == NULL)
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004989 return INVALID_HANDLE_VALUE;
4990
4991 h = CreateFileW(wn, dwDesiredAccess, dwShareMode,
4992 lpSecurityAttributes, dwCreationDisposition,
4993 dwFlagsAndAttributes, NULL);
4994 vim_free(wn);
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01004995 return h;
4996}
4997
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004998/*
4999 * Turn the dictionary "env" into a NUL separated list that can be used as the
5000 * environment argument of vim_create_process().
5001 */
Bram Moolenaarba6febd2017-10-30 21:56:23 +01005002 void
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005003win32_build_env(dict_T *env, garray_T *gap, int is_terminal)
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005004{
5005 hashitem_T *hi;
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005006 long_u todo = env != NULL ? env->dv_hashtab.ht_used : 0;
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005007 LPVOID base = GetEnvironmentStringsW();
5008
Bram Moolenaar0f873732019-12-05 20:28:46 +01005009 // for last \0
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005010 if (ga_grow(gap, 1) == FAIL)
5011 return;
5012
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005013 if (env != NULL)
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005014 {
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005015 for (hi = env->dv_hashtab.ht_array; todo > 0; ++hi)
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005016 {
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005017 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005018 {
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005019 typval_T *item = &dict_lookup(hi)->di_tv;
5020 WCHAR *wkey = enc_to_utf16((char_u *)hi->hi_key, NULL);
Bram Moolenaard155d7a2018-12-21 16:04:21 +01005021 WCHAR *wval = enc_to_utf16(tv_get_string(item), NULL);
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005022 --todo;
5023 if (wkey != NULL && wval != NULL)
5024 {
5025 size_t n;
5026 size_t lkey = wcslen(wkey);
5027 size_t lval = wcslen(wval);
Bram Moolenaar60104f12017-08-14 23:25:04 +02005028
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005029 if (ga_grow(gap, (int)(lkey + lval + 2)) != OK)
5030 continue;
5031 for (n = 0; n < lkey; n++)
5032 *((WCHAR*)gap->ga_data + gap->ga_len++) = wkey[n];
5033 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'=';
5034 for (n = 0; n < lval; n++)
5035 *((WCHAR*)gap->ga_data + gap->ga_len++) = wval[n];
5036 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
5037 }
Bram Moolenaarbdace832019-03-02 10:13:42 +01005038 vim_free(wkey);
5039 vim_free(wval);
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005040 }
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005041 }
5042 }
5043
Bram Moolenaar355757a2020-02-10 22:06:32 +01005044 if (base)
5045 {
5046 WCHAR *p = (WCHAR*) base;
5047
5048 // for last \0
5049 if (ga_grow(gap, 1) == FAIL)
5050 return;
5051
5052 while (*p != 0 || *(p + 1) != 0)
5053 {
5054 if (ga_grow(gap, 1) == OK)
5055 *((WCHAR*)gap->ga_data + gap->ga_len++) = *p;
5056 p++;
5057 }
5058 FreeEnvironmentStrings(base);
5059 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
5060 }
5061
Bram Moolenaar493359e2018-06-12 20:25:52 +02005062# if defined(FEAT_CLIENTSERVER) || defined(FEAT_TERMINAL)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005063 {
Bram Moolenaar493359e2018-06-12 20:25:52 +02005064# ifdef FEAT_CLIENTSERVER
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005065 char_u *servername = get_vim_var_str(VV_SEND_SERVER);
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005066 size_t servername_len = STRLEN(servername);
Bram Moolenaar493359e2018-06-12 20:25:52 +02005067# endif
5068# ifdef FEAT_TERMINAL
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005069 char_u *version = get_vim_var_str(VV_VERSION);
5070 size_t version_len = STRLEN(version);
Bram Moolenaar493359e2018-06-12 20:25:52 +02005071# endif
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005072 // size of "VIM_SERVERNAME=" and value,
5073 // plus "VIM_TERMINAL=" and value,
5074 // plus two terminating NULs
5075 size_t n = 0
Bram Moolenaar493359e2018-06-12 20:25:52 +02005076# ifdef FEAT_CLIENTSERVER
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005077 + 15 + servername_len
Bram Moolenaar493359e2018-06-12 20:25:52 +02005078# endif
5079# ifdef FEAT_TERMINAL
5080 + 13 + version_len + 2
5081# endif
5082 ;
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005083
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005084 if (ga_grow(gap, (int)n) == OK)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005085 {
Bram Moolenaar493359e2018-06-12 20:25:52 +02005086# ifdef FEAT_CLIENTSERVER
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005087 for (n = 0; n < 15; n++)
5088 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5089 (WCHAR)"VIM_SERVERNAME="[n];
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005090 for (n = 0; n < servername_len; n++)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005091 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5092 (WCHAR)servername[n];
5093 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
Bram Moolenaar493359e2018-06-12 20:25:52 +02005094# endif
5095# ifdef FEAT_TERMINAL
5096 if (is_terminal)
5097 {
5098 for (n = 0; n < 13; n++)
5099 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5100 (WCHAR)"VIM_TERMINAL="[n];
5101 for (n = 0; n < version_len; n++)
5102 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5103 (WCHAR)version[n];
5104 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
5105 }
5106# endif
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005107 }
5108 }
Bram Moolenaar79c6b512018-06-12 21:11:12 +02005109# endif
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005110}
5111
Bram Moolenaarb091f302019-01-19 14:37:00 +01005112/*
5113 * Create a pair of pipes.
5114 * Return TRUE for success, FALSE for failure.
5115 */
5116 static BOOL
5117create_pipe_pair(HANDLE handles[2])
5118{
5119 static LONG s;
5120 char name[64];
5121 SECURITY_ATTRIBUTES sa;
5122
5123 sprintf(name, "\\\\?\\pipe\\vim-%08lx-%08lx",
5124 GetCurrentProcessId(),
5125 InterlockedIncrement(&s));
5126
5127 // Create named pipe. Max size of named pipe is 65535.
5128 handles[1] = CreateNamedPipe(
5129 name,
5130 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
5131 PIPE_TYPE_BYTE | PIPE_NOWAIT,
Bram Moolenaar24058382019-01-24 23:11:49 +01005132 1, MAX_NAMED_PIPE_SIZE, 0, 0, NULL);
Bram Moolenaarb091f302019-01-19 14:37:00 +01005133
5134 if (handles[1] == INVALID_HANDLE_VALUE)
5135 return FALSE;
5136
5137 sa.nLength = sizeof(sa);
5138 sa.bInheritHandle = TRUE;
5139 sa.lpSecurityDescriptor = NULL;
5140
5141 handles[0] = CreateFile(name,
5142 FILE_GENERIC_READ,
5143 FILE_SHARE_READ, &sa,
5144 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
5145
5146 if (handles[0] == INVALID_HANDLE_VALUE)
5147 {
Bram Moolenaar6982f422019-02-16 16:48:01 +01005148 CloseHandle(handles[1]);
Bram Moolenaarb091f302019-01-19 14:37:00 +01005149 return FALSE;
5150 }
5151
5152 return TRUE;
5153}
5154
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005155 void
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005156mch_job_start(char *cmd, job_T *job, jobopt_T *options)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005157{
5158 STARTUPINFO si;
5159 PROCESS_INFORMATION pi;
Bram Moolenaar14207f42016-10-27 21:13:10 +02005160 HANDLE jo;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005161 SECURITY_ATTRIBUTES saAttr;
5162 channel_T *channel = NULL;
Bram Moolenaard8070362016-02-15 21:56:54 +01005163 HANDLE ifd[2];
5164 HANDLE ofd[2];
5165 HANDLE efd[2];
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005166 garray_T ga;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005167
5168 int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL;
5169 int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL;
5170 int use_null_for_err = options->jo_io[PART_ERR] == JIO_NULL;
5171 int use_file_for_in = options->jo_io[PART_IN] == JIO_FILE;
5172 int use_file_for_out = options->jo_io[PART_OUT] == JIO_FILE;
5173 int use_file_for_err = options->jo_io[PART_ERR] == JIO_FILE;
5174 int use_out_for_err = options->jo_io[PART_ERR] == JIO_OUT;
5175
5176 if (use_out_for_err && use_null_for_out)
5177 use_null_for_err = TRUE;
Bram Moolenaard8070362016-02-15 21:56:54 +01005178
5179 ifd[0] = INVALID_HANDLE_VALUE;
5180 ifd[1] = INVALID_HANDLE_VALUE;
5181 ofd[0] = INVALID_HANDLE_VALUE;
5182 ofd[1] = INVALID_HANDLE_VALUE;
5183 efd[0] = INVALID_HANDLE_VALUE;
5184 efd[1] = INVALID_HANDLE_VALUE;
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005185 ga_init2(&ga, (int)sizeof(wchar_t), 500);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005186
Bram Moolenaar14207f42016-10-27 21:13:10 +02005187 jo = CreateJobObject(NULL, NULL);
5188 if (jo == NULL)
5189 {
5190 job->jv_status = JOB_FAILED;
5191 goto failed;
5192 }
5193
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005194 if (options->jo_env != NULL)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005195 win32_build_env(options->jo_env, &ga, FALSE);
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005196
Bram Moolenaar76467df2016-02-12 19:30:26 +01005197 ZeroMemory(&pi, sizeof(pi));
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005198 ZeroMemory(&si, sizeof(si));
5199 si.cb = sizeof(si);
Bram Moolenaard8070362016-02-15 21:56:54 +01005200 si.dwFlags |= STARTF_USESHOWWINDOW;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005201 si.wShowWindow = SW_HIDE;
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005202
Bram Moolenaard8070362016-02-15 21:56:54 +01005203 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
5204 saAttr.bInheritHandle = TRUE;
5205 saAttr.lpSecurityDescriptor = NULL;
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005206
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005207 if (use_file_for_in)
5208 {
5209 char_u *fname = options->jo_io_name[PART_IN];
5210
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005211 ifd[0] = job_io_file_open(fname, GENERIC_READ,
5212 FILE_SHARE_READ | FILE_SHARE_WRITE,
5213 &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL);
5214 if (ifd[0] == INVALID_HANDLE_VALUE)
Bram Moolenaar94d01912016-03-08 13:48:51 +01005215 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005216 semsg(_(e_notopen), fname);
Bram Moolenaar94d01912016-03-08 13:48:51 +01005217 goto failed;
5218 }
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005219 }
Bram Moolenaarb091f302019-01-19 14:37:00 +01005220 else if (!use_null_for_in
5221 && (!create_pipe_pair(ifd)
5222 || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005223 goto failed;
5224
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005225 if (use_file_for_out)
5226 {
5227 char_u *fname = options->jo_io_name[PART_OUT];
5228
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005229 ofd[1] = job_io_file_open(fname, GENERIC_WRITE,
5230 FILE_SHARE_READ | FILE_SHARE_WRITE,
5231 &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL);
5232 if (ofd[1] == INVALID_HANDLE_VALUE)
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005233 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005234 semsg(_(e_notopen), fname);
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005235 goto failed;
5236 }
5237 }
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005238 else if (!use_null_for_out &&
5239 (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005240 || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005241 goto failed;
5242
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005243 if (use_file_for_err)
5244 {
5245 char_u *fname = options->jo_io_name[PART_ERR];
5246
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005247 efd[1] = job_io_file_open(fname, GENERIC_WRITE,
5248 FILE_SHARE_READ | FILE_SHARE_WRITE,
5249 &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL);
5250 if (efd[1] == INVALID_HANDLE_VALUE)
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005251 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005252 semsg(_(e_notopen), fname);
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005253 goto failed;
5254 }
5255 }
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005256 else if (!use_out_for_err && !use_null_for_err &&
5257 (!CreatePipe(&efd[0], &efd[1], &saAttr, 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005258 || !SetHandleInformation(efd[0], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaard8070362016-02-15 21:56:54 +01005259 goto failed;
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005260
Bram Moolenaard8070362016-02-15 21:56:54 +01005261 si.dwFlags |= STARTF_USESTDHANDLES;
5262 si.hStdInput = ifd[0];
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005263 si.hStdOutput = ofd[1];
5264 si.hStdError = use_out_for_err ? ofd[1] : efd[1];
5265
5266 if (!use_null_for_in || !use_null_for_out || !use_null_for_err)
5267 {
Bram Moolenaarde279892016-03-11 22:19:44 +01005268 if (options->jo_set & JO_CHANNEL)
5269 {
5270 channel = options->jo_channel;
5271 if (channel != NULL)
5272 ++channel->ch_refcount;
5273 }
5274 else
5275 channel = add_channel();
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005276 if (channel == NULL)
5277 goto failed;
5278 }
Bram Moolenaard8070362016-02-15 21:56:54 +01005279
5280 if (!vim_create_process(cmd, TRUE,
Bram Moolenaar14207f42016-10-27 21:13:10 +02005281 CREATE_SUSPENDED |
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005282 CREATE_DEFAULT_ERROR_MODE |
5283 CREATE_NEW_PROCESS_GROUP |
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005284 CREATE_UNICODE_ENVIRONMENT |
Bram Moolenaar76467df2016-02-12 19:30:26 +01005285 CREATE_NEW_CONSOLE,
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005286 &si, &pi,
5287 ga.ga_data,
5288 (char *)options->jo_cwd))
Bram Moolenaar76467df2016-02-12 19:30:26 +01005289 {
Bram Moolenaar14207f42016-10-27 21:13:10 +02005290 CloseHandle(jo);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005291 job->jv_status = JOB_FAILED;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005292 goto failed;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005293 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005294
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005295 ga_clear(&ga);
5296
Bram Moolenaar14207f42016-10-27 21:13:10 +02005297 if (!AssignProcessToJobObject(jo, pi.hProcess))
5298 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005299 // if failing, switch the way to terminate
5300 // process with TerminateProcess.
Bram Moolenaar14207f42016-10-27 21:13:10 +02005301 CloseHandle(jo);
5302 jo = NULL;
5303 }
5304 ResumeThread(pi.hThread);
Bram Moolenaar75578a32016-03-10 16:33:31 +01005305 CloseHandle(pi.hThread);
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005306 job->jv_proc_info = pi;
Bram Moolenaar14207f42016-10-27 21:13:10 +02005307 job->jv_job_object = jo;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005308 job->jv_status = JOB_STARTED;
5309
Bram Moolenaar641ad6c2016-09-01 18:32:11 +02005310 CloseHandle(ifd[0]);
5311 CloseHandle(ofd[1]);
5312 if (!use_out_for_err && !use_null_for_err)
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005313 CloseHandle(efd[1]);
Bram Moolenaard8070362016-02-15 21:56:54 +01005314
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005315 job->jv_channel = channel;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005316 if (channel != NULL)
5317 {
5318 channel_set_pipes(channel,
5319 use_file_for_in || use_null_for_in
5320 ? INVALID_FD : (sock_T)ifd[1],
5321 use_file_for_out || use_null_for_out
5322 ? INVALID_FD : (sock_T)ofd[0],
5323 use_out_for_err || use_file_for_err || use_null_for_err
5324 ? INVALID_FD : (sock_T)efd[0]);
5325 channel_set_job(channel, job, options);
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005326 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005327 return;
5328
5329failed:
Bram Moolenaard8070362016-02-15 21:56:54 +01005330 CloseHandle(ifd[0]);
5331 CloseHandle(ofd[0]);
5332 CloseHandle(efd[0]);
5333 CloseHandle(ifd[1]);
5334 CloseHandle(ofd[1]);
5335 CloseHandle(efd[1]);
Bram Moolenaarde279892016-03-11 22:19:44 +01005336 channel_unref(channel);
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005337 ga_clear(&ga);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005338}
5339
5340 char *
5341mch_job_status(job_T *job)
5342{
5343 DWORD dwExitCode = 0;
5344
Bram Moolenaar76467df2016-02-12 19:30:26 +01005345 if (!GetExitCodeProcess(job->jv_proc_info.hProcess, &dwExitCode)
5346 || dwExitCode != STILL_ACTIVE)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005347 {
Bram Moolenaareab089d2016-02-21 19:32:02 +01005348 job->jv_exitval = (int)dwExitCode;
Bram Moolenaar7df915d2016-11-17 17:25:32 +01005349 if (job->jv_status < JOB_ENDED)
Bram Moolenaar97792de2016-10-15 18:36:49 +02005350 {
5351 ch_log(job->jv_channel, "Job ended");
5352 job->jv_status = JOB_ENDED;
5353 }
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005354 return "dead";
5355 }
5356 return "run";
5357}
5358
Bram Moolenaar97792de2016-10-15 18:36:49 +02005359 job_T *
5360mch_detect_ended_job(job_T *job_list)
5361{
5362 HANDLE jobHandles[MAXIMUM_WAIT_OBJECTS];
5363 job_T *jobArray[MAXIMUM_WAIT_OBJECTS];
5364 job_T *job = job_list;
5365
5366 while (job != NULL)
5367 {
5368 DWORD n;
5369 DWORD result;
5370
5371 for (n = 0; n < MAXIMUM_WAIT_OBJECTS
5372 && job != NULL; job = job->jv_next)
5373 {
5374 if (job->jv_status == JOB_STARTED)
5375 {
5376 jobHandles[n] = job->jv_proc_info.hProcess;
5377 jobArray[n] = job;
5378 ++n;
5379 }
5380 }
5381 if (n == 0)
5382 continue;
5383 result = WaitForMultipleObjects(n, jobHandles, FALSE, 0);
5384 if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + n)
5385 {
5386 job_T *wait_job = jobArray[result - WAIT_OBJECT_0];
5387
5388 if (STRCMP(mch_job_status(wait_job), "dead") == 0)
5389 return wait_job;
5390 }
5391 }
5392 return NULL;
5393}
5394
Bram Moolenaarfb630902016-10-29 14:55:00 +02005395 static BOOL
5396terminate_all(HANDLE process, int code)
5397{
5398 PROCESSENTRY32 pe;
5399 HANDLE h = INVALID_HANDLE_VALUE;
5400 DWORD pid = GetProcessId(process);
5401
5402 if (pid != 0)
5403 {
5404 h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
5405 if (h != INVALID_HANDLE_VALUE)
5406 {
5407 pe.dwSize = sizeof(PROCESSENTRY32);
5408 if (!Process32First(h, &pe))
5409 goto theend;
5410
5411 do
5412 {
5413 if (pe.th32ParentProcessID == pid)
5414 {
5415 HANDLE ph = OpenProcess(
5416 PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
5417 if (ph != NULL)
5418 {
5419 terminate_all(ph, code);
5420 CloseHandle(ph);
5421 }
5422 }
5423 } while (Process32Next(h, &pe));
5424
5425 CloseHandle(h);
5426 }
5427 }
5428
5429theend:
5430 return TerminateProcess(process, code);
5431}
5432
5433/*
5434 * Send a (deadly) signal to "job".
5435 * Return FAIL if it didn't work.
5436 */
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005437 int
Bram Moolenaar2d33e902017-08-11 16:31:54 +02005438mch_signal_job(job_T *job, char_u *how)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005439{
Bram Moolenaar923d9262016-02-25 20:56:01 +01005440 int ret;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005441
Bram Moolenaar923d9262016-02-25 20:56:01 +01005442 if (STRCMP(how, "term") == 0 || STRCMP(how, "kill") == 0 || *how == NUL)
Bram Moolenaar76467df2016-02-12 19:30:26 +01005443 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005444 // deadly signal
Bram Moolenaar14207f42016-10-27 21:13:10 +02005445 if (job->jv_job_object != NULL)
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01005446 {
5447 if (job->jv_channel != NULL && job->jv_channel->ch_anonymous_pipe)
5448 job->jv_channel->ch_killing = TRUE;
Bram Moolenaarb3e195c2020-02-10 21:32:19 +01005449 return TerminateJobObject(job->jv_job_object, -1) ? OK : FAIL;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01005450 }
Bram Moolenaarb3e195c2020-02-10 21:32:19 +01005451 return terminate_all(job->jv_proc_info.hProcess, -1) ? OK : FAIL;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005452 }
5453
5454 if (!AttachConsole(job->jv_proc_info.dwProcessId))
5455 return FAIL;
5456 ret = GenerateConsoleCtrlEvent(
Bram Moolenaar923d9262016-02-25 20:56:01 +01005457 STRCMP(how, "int") == 0 ? CTRL_C_EVENT : CTRL_BREAK_EVENT,
5458 job->jv_proc_info.dwProcessId)
5459 ? OK : FAIL;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005460 FreeConsole();
5461 return ret;
5462}
5463
5464/*
5465 * Clear the data related to "job".
5466 */
5467 void
5468mch_clear_job(job_T *job)
5469{
5470 if (job->jv_status != JOB_FAILED)
5471 {
Bram Moolenaar14207f42016-10-27 21:13:10 +02005472 if (job->jv_job_object != NULL)
5473 CloseHandle(job->jv_job_object);
Bram Moolenaar76467df2016-02-12 19:30:26 +01005474 CloseHandle(job->jv_proc_info.hProcess);
5475 }
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005476}
5477#endif
5478
Bram Moolenaar071d4272004-06-13 20:20:40 +00005479
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005480#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005481
5482/*
5483 * Start termcap mode
5484 */
5485 static void
5486termcap_mode_start(void)
5487{
5488 DWORD cmodein;
5489
5490 if (g_fTermcapMode)
5491 return;
5492
Bram Moolenaar81ccbf12020-04-15 21:05:30 +02005493 if (!p_rs && USE_VTP)
5494 vtp_printf("\033[?1049h");
5495
Bram Moolenaar071d4272004-06-13 20:20:40 +00005496 SaveConsoleBuffer(&g_cbNonTermcap);
5497
5498 if (g_cbTermcap.IsValid)
5499 {
5500 /*
5501 * We've been in termcap mode before. Restore certain screen
5502 * characteristics, including the buffer size and the window
5503 * size. Since we will be redrawing the screen, we don't need
5504 * to restore the actual contents of the buffer.
5505 */
5506 RestoreConsoleBuffer(&g_cbTermcap, FALSE);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005507 reset_console_color_rgb();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005508 SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow);
5509 Rows = g_cbTermcap.Info.dwSize.Y;
5510 Columns = g_cbTermcap.Info.dwSize.X;
5511 }
5512 else
5513 {
5514 /*
5515 * This is our first time entering termcap mode. Clear the console
5516 * screen buffer, and resize the buffer to match the current window
5517 * size. We will use this as the size of our editing environment.
5518 */
5519 ClearConsoleBuffer(g_attrCurrent);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005520 set_console_color_rgb();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005521 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
5522 }
5523
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01005524# ifdef FEAT_TITLE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005525 resettitle();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01005526# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005527
5528 GetConsoleMode(g_hConIn, &cmodein);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005529 if (g_fMouseActive)
5530 cmodein |= ENABLE_MOUSE_INPUT;
5531 else
5532 cmodein &= ~ENABLE_MOUSE_INPUT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005533 cmodein |= ENABLE_WINDOW_INPUT;
5534 SetConsoleMode(g_hConIn, cmodein);
5535
5536 redraw_later_clear();
5537 g_fTermcapMode = TRUE;
5538}
5539
5540
5541/*
5542 * End termcap mode
5543 */
5544 static void
5545termcap_mode_end(void)
5546{
5547 DWORD cmodein;
5548 ConsoleBuffer *cb;
5549 COORD coord;
5550 DWORD dwDummy;
5551
5552 if (!g_fTermcapMode)
5553 return;
5554
5555 SaveConsoleBuffer(&g_cbTermcap);
5556
5557 GetConsoleMode(g_hConIn, &cmodein);
5558 cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
5559 SetConsoleMode(g_hConIn, cmodein);
5560
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01005561# ifdef FEAT_RESTORE_ORIG_SCREEN
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01005562 cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01005563# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005564 cb = &g_cbNonTermcap;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01005565# endif
Bram Moolenaar81ccbf12020-04-15 21:05:30 +02005566 RestoreConsoleBuffer(cb, p_rs);
Bram Moolenaardf543822020-01-30 11:53:59 +01005567 restore_console_color_rgb();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005568 SetConsoleCursorInfo(g_hConOut, &g_cci);
5569
5570 if (p_rs || exiting)
5571 {
5572 /*
5573 * Clear anything that happens to be on the current line.
5574 */
5575 coord.X = 0;
5576 coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1));
5577 FillConsoleOutputCharacter(g_hConOut, ' ',
5578 cb->Info.dwSize.X, coord, &dwDummy);
5579 /*
5580 * The following is just for aesthetics. If we are exiting without
5581 * restoring the screen, then we want to have a prompt string
5582 * appear at the bottom line. However, the command interpreter
5583 * seems to always advance the cursor one line before displaying
5584 * the prompt string, which causes the screen to scroll. To
5585 * counter this, move the cursor up one line before exiting.
5586 */
5587 if (exiting && !p_rs)
5588 coord.Y--;
5589 /*
5590 * Position the cursor at the leftmost column of the desired row.
5591 */
Bram Moolenaar81ccbf12020-04-15 21:05:30 +02005592 SetConsoleCursorPosition(g_hConOut, coord);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005593 }
5594
Bram Moolenaar81ccbf12020-04-15 21:05:30 +02005595 if (!p_rs && USE_VTP)
Bram Moolenaar0afdcf82020-04-01 18:29:10 +02005596 vtp_printf("\033[?1049l");
5597
Bram Moolenaar071d4272004-06-13 20:20:40 +00005598 g_fTermcapMode = FALSE;
5599}
Bram Moolenaar0f873732019-12-05 20:28:46 +01005600#endif // FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00005601
5602
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005603#if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005604 void
5605mch_write(
Bram Moolenaar1266d672017-02-01 13:43:36 +01005606 char_u *s UNUSED,
5607 int len UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005608{
Bram Moolenaar0f873732019-12-05 20:28:46 +01005609 // never used
Bram Moolenaar071d4272004-06-13 20:20:40 +00005610}
5611
5612#else
5613
5614/*
5615 * clear `n' chars, starting from `coord'
5616 */
5617 static void
5618clear_chars(
5619 COORD coord,
5620 DWORD n)
5621{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005622 if (!USE_VTP)
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02005623 {
5624 DWORD dwDummy;
5625
5626 FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
5627 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord,
5628 &dwDummy);
5629 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005630 else
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02005631 {
5632 set_console_color_rgb();
5633 gotoxy(coord.X + 1, coord.Y + 1);
5634 vtp_printf("\033[%dX", n);
5635 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005636}
5637
5638
5639/*
5640 * Clear the screen
5641 */
5642 static void
5643clear_screen(void)
5644{
5645 g_coord.X = g_coord.Y = 0;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005646
5647 if (!USE_VTP)
5648 clear_chars(g_coord, Rows * Columns);
5649 else
5650 {
5651 set_console_color_rgb();
5652 gotoxy(1, 1);
5653 vtp_printf("\033[2J");
5654 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005655}
5656
5657
5658/*
5659 * Clear to end of display
5660 */
5661 static void
5662clear_to_end_of_display(void)
5663{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005664 COORD save = g_coord;
5665
5666 if (!USE_VTP)
5667 clear_chars(g_coord, (Rows - g_coord.Y - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005668 * Columns + (Columns - g_coord.X));
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005669 else
5670 {
5671 set_console_color_rgb();
5672 gotoxy(g_coord.X + 1, g_coord.Y + 1);
5673 vtp_printf("\033[0J");
5674
5675 gotoxy(save.X + 1, save.Y + 1);
5676 g_coord = save;
5677 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005678}
5679
5680
5681/*
5682 * Clear to end of line
5683 */
5684 static void
5685clear_to_end_of_line(void)
5686{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005687 COORD save = g_coord;
5688
5689 if (!USE_VTP)
5690 clear_chars(g_coord, Columns - g_coord.X);
5691 else
5692 {
5693 set_console_color_rgb();
5694 gotoxy(g_coord.X + 1, g_coord.Y + 1);
5695 vtp_printf("\033[0K");
5696
5697 gotoxy(save.X + 1, save.Y + 1);
5698 g_coord = save;
5699 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005700}
5701
5702
5703/*
5704 * Scroll the scroll region up by `cLines' lines
5705 */
5706 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005707scroll(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005708{
5709 COORD oldcoord = g_coord;
5710
5711 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
5712 delete_lines(cLines);
5713
5714 g_coord = oldcoord;
5715}
5716
5717
5718/*
5719 * Set the scroll region
5720 */
5721 static void
5722set_scroll_region(
5723 unsigned left,
5724 unsigned top,
5725 unsigned right,
5726 unsigned bottom)
5727{
5728 if (left >= right
5729 || top >= bottom
5730 || right > (unsigned) Columns - 1
5731 || bottom > (unsigned) Rows - 1)
5732 return;
5733
5734 g_srScrollRegion.Left = left;
5735 g_srScrollRegion.Top = top;
5736 g_srScrollRegion.Right = right;
5737 g_srScrollRegion.Bottom = bottom;
Bram Moolenaar6982f422019-02-16 16:48:01 +01005738}
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005739
Bram Moolenaar6982f422019-02-16 16:48:01 +01005740 static void
5741set_scroll_region_tb(
5742 unsigned top,
5743 unsigned bottom)
5744{
5745 if (top >= bottom || bottom > (unsigned)Rows - 1)
5746 return;
5747
5748 g_srScrollRegion.Top = top;
5749 g_srScrollRegion.Bottom = bottom;
5750}
5751
5752 static void
5753set_scroll_region_lr(
5754 unsigned left,
5755 unsigned right)
5756{
5757 if (left >= right || right > (unsigned)Columns - 1)
5758 return;
5759
5760 g_srScrollRegion.Left = left;
5761 g_srScrollRegion.Right = right;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005762}
5763
5764
5765/*
5766 * Insert `cLines' lines at the current cursor position
5767 */
5768 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005769insert_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005770{
Bram Moolenaar6982f422019-02-16 16:48:01 +01005771 SMALL_RECT source, clip;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005772 COORD dest;
5773 CHAR_INFO fill;
5774
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01005775 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
5776
Bram Moolenaar6982f422019-02-16 16:48:01 +01005777 dest.X = g_srScrollRegion.Left;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005778 dest.Y = g_coord.Y + cLines;
5779
Bram Moolenaar6982f422019-02-16 16:48:01 +01005780 source.Left = g_srScrollRegion.Left;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005781 source.Top = g_coord.Y;
5782 source.Right = g_srScrollRegion.Right;
5783 source.Bottom = g_srScrollRegion.Bottom - cLines;
5784
Bram Moolenaar6982f422019-02-16 16:48:01 +01005785 clip.Left = g_srScrollRegion.Left;
5786 clip.Top = g_coord.Y;
5787 clip.Right = g_srScrollRegion.Right;
5788 clip.Bottom = g_srScrollRegion.Bottom;
5789
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01005790 fill.Char.AsciiChar = ' ';
5791 if (!USE_VTP)
5792 fill.Attributes = g_attrCurrent;
5793 else
5794 fill.Attributes = g_attrDefault;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005795
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01005796 set_console_color_rgb();
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005797
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01005798 ScrollConsoleScreenBuffer(g_hConOut, &source, &clip, dest, &fill);
5799
Bram Moolenaar6982f422019-02-16 16:48:01 +01005800 // Here we have to deal with a win32 console flake: If the scroll
5801 // region looks like abc and we scroll c to a and fill with d we get
5802 // cbd... if we scroll block c one line at a time to a, we get cdd...
5803 // vim expects cdd consistently... So we have to deal with that
5804 // here... (this also occurs scrolling the same way in the other
5805 // direction).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005806
5807 if (source.Bottom < dest.Y)
5808 {
5809 COORD coord;
Bram Moolenaar6982f422019-02-16 16:48:01 +01005810 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005811
Bram Moolenaar6982f422019-02-16 16:48:01 +01005812 coord.X = source.Left;
5813 for (i = clip.Top; i < dest.Y; ++i)
5814 {
5815 coord.Y = i;
5816 clear_chars(coord, source.Right - source.Left + 1);
5817 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005818 }
Bram Moolenaar06b7b582020-05-30 17:49:25 +02005819
5820 if (USE_WT)
5821 {
5822 COORD coord;
5823 int i;
5824
5825 coord.X = source.Left;
5826 for (i = source.Top; i < dest.Y; ++i)
5827 {
5828 coord.Y = i;
5829 clear_chars(coord, source.Right - source.Left + 1);
5830 }
5831 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005832}
5833
5834
5835/*
5836 * Delete `cLines' lines at the current cursor position
5837 */
5838 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005839delete_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005840{
Bram Moolenaar6982f422019-02-16 16:48:01 +01005841 SMALL_RECT source, clip;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005842 COORD dest;
5843 CHAR_INFO fill;
5844 int nb;
5845
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01005846 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
5847
Bram Moolenaar6982f422019-02-16 16:48:01 +01005848 dest.X = g_srScrollRegion.Left;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005849 dest.Y = g_coord.Y;
5850
Bram Moolenaar6982f422019-02-16 16:48:01 +01005851 source.Left = g_srScrollRegion.Left;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005852 source.Top = g_coord.Y + cLines;
5853 source.Right = g_srScrollRegion.Right;
5854 source.Bottom = g_srScrollRegion.Bottom;
5855
Bram Moolenaar6982f422019-02-16 16:48:01 +01005856 clip.Left = g_srScrollRegion.Left;
5857 clip.Top = g_coord.Y;
5858 clip.Right = g_srScrollRegion.Right;
5859 clip.Bottom = g_srScrollRegion.Bottom;
5860
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01005861 fill.Char.AsciiChar = ' ';
5862 if (!USE_VTP)
5863 fill.Attributes = g_attrCurrent;
5864 else
5865 fill.Attributes = g_attrDefault;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005866
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01005867 set_console_color_rgb();
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005868
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01005869 ScrollConsoleScreenBuffer(g_hConOut, &source, &clip, dest, &fill);
5870
Bram Moolenaar6982f422019-02-16 16:48:01 +01005871 // Here we have to deal with a win32 console flake; See insert_lines()
5872 // above.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005873
5874 nb = dest.Y + (source.Bottom - source.Top) + 1;
5875
5876 if (nb < source.Top)
5877 {
5878 COORD coord;
Bram Moolenaar6982f422019-02-16 16:48:01 +01005879 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005880
Bram Moolenaar6982f422019-02-16 16:48:01 +01005881 coord.X = source.Left;
5882 for (i = nb; i < clip.Bottom; ++i)
5883 {
5884 coord.Y = i;
5885 clear_chars(coord, source.Right - source.Left + 1);
5886 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005887 }
Bram Moolenaar06b7b582020-05-30 17:49:25 +02005888
5889 if (USE_WT)
5890 {
5891 COORD coord;
5892 int i;
5893
5894 coord.X = source.Left;
5895 for (i = nb; i <= source.Bottom; ++i)
5896 {
5897 coord.Y = i;
5898 clear_chars(coord, source.Right - source.Left + 1);
5899 }
5900 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005901}
5902
5903
5904/*
Bram Moolenaar2313b612019-09-27 14:14:32 +02005905 * Set the cursor position to (x,y) (1-based).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005906 */
5907 static void
5908gotoxy(
5909 unsigned x,
5910 unsigned y)
5911{
5912 if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows)
5913 return;
5914
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005915 if (!USE_VTP)
Bram Moolenaar2313b612019-09-27 14:14:32 +02005916 {
Bram Moolenaar82e743c2020-03-26 15:39:53 +01005917 // There are reports of double-width characters not displayed
5918 // correctly. This workaround should fix it, similar to how it's done
5919 // for VTP.
5920 g_coord.X = 0;
5921 SetConsoleCursorPosition(g_hConOut, g_coord);
5922
Bram Moolenaar2313b612019-09-27 14:14:32 +02005923 // external cursor coords are 1-based; internal are 0-based
5924 g_coord.X = x - 1;
5925 g_coord.Y = y - 1;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005926 SetConsoleCursorPosition(g_hConOut, g_coord);
Bram Moolenaar2313b612019-09-27 14:14:32 +02005927 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005928 else
Bram Moolenaar2313b612019-09-27 14:14:32 +02005929 {
5930 // Move the cursor to the left edge of the screen to prevent screen
Bram Moolenaara1299742019-10-10 16:36:00 +02005931 // destruction. Insider build bug. Always enabled because it's cheap
5932 // and avoids mistakes with recognizing the build.
5933 vtp_printf("\033[%d;%dH", g_coord.Y + 1, 1);
Bram Moolenaar2313b612019-09-27 14:14:32 +02005934
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005935 vtp_printf("\033[%d;%dH", y, x);
Bram Moolenaar2313b612019-09-27 14:14:32 +02005936
5937 g_coord.X = x - 1;
5938 g_coord.Y = y - 1;
5939 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005940}
5941
5942
5943/*
5944 * Set the current text attribute = (foreground | background)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005945 * See ../runtime/doc/os_win32.txt for the numbers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005946 */
5947 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005948textattr(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005949{
Bram Moolenaar6383b922015-03-24 17:12:19 +01005950 g_attrCurrent = wAttr & 0xff;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005951
5952 SetConsoleTextAttribute(g_hConOut, wAttr);
5953}
5954
5955
5956 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005957textcolor(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005958{
Bram Moolenaar6383b922015-03-24 17:12:19 +01005959 g_attrCurrent = (g_attrCurrent & 0xf0) + (wAttr & 0x0f);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005960
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005961 if (!USE_VTP)
5962 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
5963 else
5964 vtp_sgr_bulk(wAttr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005965}
5966
5967
5968 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005969textbackground(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005970{
Bram Moolenaar6383b922015-03-24 17:12:19 +01005971 g_attrCurrent = (g_attrCurrent & 0x0f) + ((wAttr & 0x0f) << 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005972
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005973 if (!USE_VTP)
5974 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
5975 else
5976 vtp_sgr_bulk(wAttr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005977}
5978
5979
5980/*
5981 * restore the default text attribute (whatever we started with)
5982 */
5983 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005984normvideo(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005985{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01005986 if (!USE_VTP)
5987 textattr(g_attrDefault);
5988 else
5989 vtp_sgr_bulk(0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005990}
5991
5992
5993static WORD g_attrPreStandout = 0;
5994
5995/*
5996 * Make the text standout, by brightening it
5997 */
5998 static void
5999standout(void)
6000{
6001 g_attrPreStandout = g_attrCurrent;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006002
Bram Moolenaar071d4272004-06-13 20:20:40 +00006003 textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
6004}
6005
6006
6007/*
6008 * Turn off standout mode
6009 */
6010 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006011standend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006012{
6013 if (g_attrPreStandout)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006014 textattr(g_attrPreStandout);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006015
6016 g_attrPreStandout = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006017}
6018
6019
6020/*
Bram Moolenaarff1d0d42007-05-10 17:24:16 +00006021 * Set normal fg/bg color, based on T_ME. Called when t_me has been set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006022 */
6023 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006024mch_set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006025{
6026 char_u *p;
6027 int n;
6028
6029 cterm_normal_fg_color = (g_attrDefault & 0xf) + 1;
6030 cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006031 if (
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006032# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006033 !p_tgc &&
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006034# endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006035 T_ME[0] == ESC && T_ME[1] == '|')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006036 {
6037 p = T_ME + 2;
6038 n = getdigits(&p);
6039 if (*p == 'm' && n > 0)
6040 {
6041 cterm_normal_fg_color = (n & 0xf) + 1;
6042 cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
6043 }
6044 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006045# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006046 cterm_normal_fg_gui_color = INVALCOLOR;
6047 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006048# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006049}
6050
6051
6052/*
6053 * visual bell: flash the screen
6054 */
6055 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006056visual_bell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006057{
6058 COORD coordOrigin = {0, 0};
6059 WORD attrFlash = ~g_attrCurrent & 0xff;
6060
6061 DWORD dwDummy;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006062 LPWORD oldattrs = ALLOC_MULT(WORD, Rows * Columns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006063
6064 if (oldattrs == NULL)
6065 return;
6066 ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
6067 coordOrigin, &dwDummy);
6068 FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns,
6069 coordOrigin, &dwDummy);
6070
Bram Moolenaar0f873732019-12-05 20:28:46 +01006071 Sleep(15); // wait for 15 msec
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006072 if (!USE_VTP)
6073 WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006074 coordOrigin, &dwDummy);
6075 vim_free(oldattrs);
6076}
6077
6078
6079/*
6080 * Make the cursor visible or invisible
6081 */
6082 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006083cursor_visible(BOOL fVisible)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006084{
6085 s_cursor_visible = fVisible;
Bram Moolenaar2695de62020-04-17 21:13:01 +02006086
6087 if (USE_VTP)
6088 vtp_printf("\033[?25%c", fVisible ? 'h' : 'l');
6089
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006090# ifdef MCH_CURSOR_SHAPE
Bram Moolenaar071d4272004-06-13 20:20:40 +00006091 mch_update_cursor();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006092# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006093}
6094
6095
6096/*
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006097 * Write "cbToWrite" bytes in `pchBuf' to the screen.
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006098 * Returns the number of bytes actually written (at least one).
Bram Moolenaar071d4272004-06-13 20:20:40 +00006099 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006100 static DWORD
Bram Moolenaar071d4272004-06-13 20:20:40 +00006101write_chars(
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006102 char_u *pchBuf,
6103 DWORD cbToWrite)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006104{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006105 COORD coord = g_coord;
6106 DWORD written;
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006107 DWORD n, cchwritten;
6108 static DWORD cells;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006109 static WCHAR *unicodebuf = NULL;
6110 static int unibuflen = 0;
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006111 static int length;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006112 int cp = enc_utf8 ? CP_UTF8 : enc_codepage;
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006113 static WCHAR *utf8spbuf = NULL;
6114 static int utf8splength;
6115 static DWORD utf8spcells;
6116 static WCHAR **utf8usingbuf = &unicodebuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006117
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006118 if (cbToWrite != 1 || *pchBuf != ' ' || !enc_utf8)
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006119 {
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006120 utf8usingbuf = &unicodebuf;
6121 do
6122 {
6123 length = MultiByteToWideChar(cp, 0, (LPCSTR)pchBuf, cbToWrite,
6124 unicodebuf, unibuflen);
6125 if (length && length <= unibuflen)
6126 break;
6127 vim_free(unicodebuf);
6128 unicodebuf = length ? LALLOC_MULT(WCHAR, length) : NULL;
6129 unibuflen = unibuflen ? 0 : length;
6130 } while(1);
6131 cells = mb_string2cells(pchBuf, cbToWrite);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006132 }
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006133 else // cbToWrite == 1 && *pchBuf == ' ' && enc_utf8
6134 {
6135 if (utf8usingbuf != &utf8spbuf)
6136 {
6137 if (utf8spbuf == NULL)
6138 {
6139 cells = mb_string2cells((char_u *)" ", 1);
6140 length = MultiByteToWideChar(CP_UTF8, 0, " ", 1, NULL, 0);
6141 utf8spbuf = LALLOC_MULT(WCHAR, length);
6142 if (utf8spbuf != NULL)
6143 {
6144 MultiByteToWideChar(CP_UTF8, 0, " ", 1, utf8spbuf, length);
6145 utf8usingbuf = &utf8spbuf;
6146 utf8splength = length;
6147 utf8spcells = cells;
6148 }
6149 }
6150 else
6151 {
6152 utf8usingbuf = &utf8spbuf;
6153 length = utf8splength;
6154 cells = utf8spcells;
6155 }
6156 }
6157 }
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006158
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006159 if (!USE_VTP)
6160 {
6161 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cells,
6162 coord, &written);
6163 // When writing fails or didn't write a single character, pretend one
6164 // character was written, otherwise we get stuck.
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006165 if (WriteConsoleOutputCharacterW(g_hConOut, *utf8usingbuf, length,
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006166 coord, &cchwritten) == 0
6167 || cchwritten == 0 || cchwritten == (DWORD)-1)
6168 cchwritten = 1;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006169 }
6170 else
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006171 {
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006172 if (WriteConsoleW(g_hConOut, *utf8usingbuf, length, &cchwritten,
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006173 NULL) == 0 || cchwritten == 0)
6174 cchwritten = 1;
6175 }
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006176
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006177 if (cchwritten == length)
6178 {
6179 written = cbToWrite;
6180 g_coord.X += (SHORT)cells;
6181 }
6182 else
6183 {
6184 char_u *p = pchBuf;
6185 for (n = 0; n < cchwritten; n++)
6186 MB_CPTR_ADV(p);
6187 written = p - pchBuf;
6188 g_coord.X += (SHORT)mb_string2cells(pchBuf, written);
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006189 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006190
6191 while (g_coord.X > g_srScrollRegion.Right)
6192 {
6193 g_coord.X -= (SHORT) Columns;
6194 if (g_coord.Y < g_srScrollRegion.Bottom)
6195 g_coord.Y++;
6196 }
6197
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006198 // Cursor under VTP is always in the correct position, no need to reset.
6199 if (!USE_VTP)
6200 gotoxy(g_coord.X + 1, g_coord.Y + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006201
6202 return written;
6203}
6204
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006205 static char_u *
6206get_seq(
6207 int *args,
6208 int *count,
6209 char_u *head)
6210{
6211 int argc;
6212 char_u *p;
6213
6214 if (head == NULL || *head != '\033')
6215 return NULL;
6216
6217 argc = 0;
6218 p = head;
6219 ++p;
6220 do
6221 {
6222 ++p;
6223 args[argc] = getdigits(&p);
6224 argc += (argc < 15) ? 1 : 0;
6225 } while (*p == ';');
6226 *count = argc;
6227
6228 return p;
6229}
6230
6231 static char_u *
6232get_sgr(
6233 int *args,
6234 int *count,
6235 char_u *head)
6236{
6237 char_u *p = get_seq(args, count, head);
6238
6239 return (p && *p == 'm') ? ++p : NULL;
6240}
6241
6242/*
6243 * Pointer to next if SGR (^[[n;2;*;*;*m), NULL otherwise.
6244 */
6245 static char_u *
6246sgrn2(
6247 char_u *head,
6248 int n)
6249{
6250 int argc;
6251 int args[16];
6252 char_u *p = get_sgr(args, &argc, head);
6253
6254 return p && argc == 5 && args[0] == n && args[1] == 2 ? p : NULL;
6255}
6256
6257/*
6258 * Pointer to next if SGR(^[[nm)<space>ESC, NULL otherwise.
6259 */
6260 static char_u *
6261sgrnc(
6262 char_u *head,
6263 int n)
6264{
6265 int argc;
6266 int args[16];
6267 char_u *p = get_sgr(args, &argc, head);
6268
6269 return p && argc == 1 && args[0] == n && (p = skipwhite(p)) && *p == '\033'
6270 ? p : NULL;
6271}
6272
6273 static char_u *
6274skipblank(char_u *q)
6275{
6276 char_u *p = q;
6277
6278 while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
6279 ++p;
6280 return p;
6281}
6282
6283/*
6284 * Pointer to the next if any whitespace that may follow SGR is ESC, otherwise
6285 * NULL.
6286 */
6287 static char_u *
6288sgrn2c(
6289 char_u *head,
6290 int n)
6291{
6292 char_u *p = sgrn2(head, n);
6293
6294 return p && *p != NUL && (p = skipblank(p)) && *p == '\033' ? p : NULL;
6295}
6296
6297/*
6298 * If there is only a newline between the sequence immediately following it,
6299 * a pointer to the character following the newline is returned.
6300 * Otherwise NULL.
6301 */
6302 static char_u *
6303sgrn2cn(
6304 char_u *head,
6305 int n)
6306{
6307 char_u *p = sgrn2(head, n);
6308
6309 return p && p[0] == 0x0a && p[1] == '\033' ? ++p : NULL;
6310}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006311
6312/*
6313 * mch_write(): write the output buffer to the screen, translating ESC
6314 * sequences into calls to console output routines.
6315 */
6316 void
6317mch_write(
6318 char_u *s,
6319 int len)
6320{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006321# ifdef VIMDLL
6322 if (gui.in_use)
6323 return;
6324# endif
6325
Bram Moolenaar071d4272004-06-13 20:20:40 +00006326 s[len] = NUL;
6327
6328 if (!term_console)
6329 {
6330 write(1, s, (unsigned)len);
6331 return;
6332 }
6333
Bram Moolenaar0f873732019-12-05 20:28:46 +01006334 // translate ESC | sequences into faked bios calls
Bram Moolenaar071d4272004-06-13 20:20:40 +00006335 while (len--)
6336 {
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006337 int prefix = -1;
6338 char_u ch;
6339
6340 // While processing a sequence, on rare occasions it seems that another
6341 // sequence may be inserted asynchronously.
6342 if (len < 0)
6343 {
6344 redraw_all_later(CLEAR);
6345 return;
6346 }
6347
6348 while((ch = s[++prefix]))
6349 if (ch <= 0x1e && !(ch != '\n' && ch != '\r' && ch != '\b'
6350 && ch != '\a' && ch != '\033'))
6351 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006352
6353 if (p_wd)
6354 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006355 WaitForChar(p_wd, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006356 if (prefix != 0)
6357 prefix = 1;
6358 }
6359
6360 if (prefix != 0)
6361 {
6362 DWORD nWritten;
6363
6364 nWritten = write_chars(s, prefix);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006365# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00006366 if (fdDump)
6367 {
6368 fputc('>', fdDump);
6369 fwrite(s, sizeof(char_u), nWritten, fdDump);
6370 fputs("<\n", fdDump);
6371 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006372# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006373 len -= (nWritten - 1);
6374 s += nWritten;
6375 }
6376 else if (s[0] == '\n')
6377 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006378 // \n, newline: go to the beginning of the next line or scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00006379 if (g_coord.Y == g_srScrollRegion.Bottom)
6380 {
6381 scroll(1);
6382 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
6383 }
6384 else
6385 {
6386 gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
6387 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006388# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00006389 if (fdDump)
6390 fputs("\\n\n", fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006391# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006392 s++;
6393 }
6394 else if (s[0] == '\r')
6395 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006396 // \r, carriage return: go to beginning of line
Bram Moolenaar071d4272004-06-13 20:20:40 +00006397 gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006398# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00006399 if (fdDump)
6400 fputs("\\r\n", fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006401# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006402 s++;
6403 }
6404 else if (s[0] == '\b')
6405 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006406 // \b, backspace: move cursor one position left
Bram Moolenaar071d4272004-06-13 20:20:40 +00006407 if (g_coord.X > g_srScrollRegion.Left)
6408 g_coord.X--;
6409 else if (g_coord.Y > g_srScrollRegion.Top)
6410 {
6411 g_coord.X = g_srScrollRegion.Right;
6412 g_coord.Y--;
6413 }
6414 gotoxy(g_coord.X + 1, g_coord.Y + 1);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006415# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00006416 if (fdDump)
6417 fputs("\\b\n", fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006418# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006419 s++;
6420 }
6421 else if (s[0] == '\a')
6422 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006423 // \a, bell
Bram Moolenaar071d4272004-06-13 20:20:40 +00006424 MessageBeep(0xFFFFFFFF);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006425# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00006426 if (fdDump)
6427 fputs("\\a\n", fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006428# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006429 s++;
6430 }
6431 else if (s[0] == ESC && len >= 3-1 && s[1] == '|')
6432 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006433# ifdef MCH_WRITE_DUMP
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006434 char_u *old_s = s;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006435# endif
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006436 char_u *p;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006437 int arg1 = 0, arg2 = 0, argc = 0, args[16];
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006438 char_u *sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006439
6440 switch (s[2])
6441 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006442 case '0': case '1': case '2': case '3': case '4':
6443 case '5': case '6': case '7': case '8': case '9':
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006444 if (*(p = get_seq(args, &argc, s)) != 'm')
6445 goto notsgr;
6446
6447 p = s;
6448
6449 // Handling frequent optional sequences. Output to the screen
6450 // takes too long, so do not output as much as possible.
6451
6452 // If resetFG,FG,BG,<cr>,BG,FG are connected, the preceding
6453 // resetFG,FG,BG are omitted.
6454 if (sgrn2(sgrn2(sgrn2cn(sgrn2(sgrnc(p, 39), 38), 48), 48), 38))
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006455 {
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006456 p = sgrn2(sgrn2(sgrnc(p, 39), 38), 48);
6457 len = len + 1 - (int)(p - s);
6458 s = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006459 break;
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006460 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006461
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006462 // If FG,BG,BG,FG of SGR are connected, the first FG can be
6463 // omitted.
6464 if (sgrn2(sgrn2(sgrn2c((sp = sgrn2(p, 38)), 48), 48), 38))
6465 p = sp;
6466
6467 // If FG,BG,FG,BG of SGR are connected, the first FG can be
6468 // omitted.
6469 if (sgrn2(sgrn2(sgrn2c((sp = sgrn2(p, 38)), 48), 38), 48))
6470 p = sp;
6471
6472 // If BG,BG of SGR are connected, the first BG can be omitted.
6473 if (sgrn2((sp = sgrn2(p, 48)), 48))
6474 p = sp;
6475
6476 // If restoreFG and FG are connected, the restoreFG can be
6477 // omitted.
6478 if (sgrn2((sp = sgrnc(p, 39)), 38))
6479 p = sp;
6480
6481 p = get_seq(args, &argc, p);
6482
6483notsgr:
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006484 arg1 = args[0];
6485 arg2 = args[1];
6486 if (*p == 'm')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006487 {
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006488 if (argc == 1 && args[0] == 0)
6489 normvideo();
6490 else if (argc == 1)
6491 {
6492 if (USE_VTP)
6493 textcolor((WORD) arg1);
6494 else
6495 textattr((WORD) arg1);
6496 }
6497 else if (USE_VTP)
6498 vtp_sgr_bulks(argc, args);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006499 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006500 else if (argc == 2 && *p == 'H')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006501 {
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006502 gotoxy(arg2, arg1);
6503 }
6504 else if (argc == 2 && *p == 'r')
6505 {
6506 set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
6507 }
Bram Moolenaar6982f422019-02-16 16:48:01 +01006508 else if (argc == 2 && *p == 'R')
6509 {
6510 set_scroll_region_tb(arg1, arg2);
6511 }
6512 else if (argc == 2 && *p == 'V')
6513 {
6514 set_scroll_region_lr(arg1, arg2);
6515 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006516 else if (argc == 1 && *p == 'A')
6517 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006518 gotoxy(g_coord.X + 1,
6519 max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
6520 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006521 else if (argc == 1 && *p == 'b')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006522 {
6523 textbackground((WORD) arg1);
6524 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006525 else if (argc == 1 && *p == 'C')
6526 {
6527 gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
6528 g_coord.Y + 1);
6529 }
6530 else if (argc == 1 && *p == 'f')
6531 {
6532 textcolor((WORD) arg1);
6533 }
6534 else if (argc == 1 && *p == 'H')
6535 {
6536 gotoxy(1, arg1);
6537 }
6538 else if (argc == 1 && *p == 'L')
6539 {
6540 insert_lines(arg1);
6541 }
6542 else if (argc == 1 && *p == 'M')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006543 {
6544 delete_lines(arg1);
6545 }
6546
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00006547 len -= (int)(p - s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006548 s = p + 1;
6549 break;
6550
Bram Moolenaar071d4272004-06-13 20:20:40 +00006551 case 'A':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006552 gotoxy(g_coord.X + 1,
6553 max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
6554 goto got3;
6555
6556 case 'B':
6557 visual_bell();
6558 goto got3;
6559
6560 case 'C':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006561 gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
6562 g_coord.Y + 1);
6563 goto got3;
6564
6565 case 'E':
6566 termcap_mode_end();
6567 goto got3;
6568
6569 case 'F':
6570 standout();
6571 goto got3;
6572
6573 case 'f':
6574 standend();
6575 goto got3;
6576
6577 case 'H':
6578 gotoxy(1, 1);
6579 goto got3;
6580
6581 case 'j':
6582 clear_to_end_of_display();
6583 goto got3;
6584
6585 case 'J':
6586 clear_screen();
6587 goto got3;
6588
6589 case 'K':
6590 clear_to_end_of_line();
6591 goto got3;
6592
6593 case 'L':
6594 insert_lines(1);
6595 goto got3;
6596
6597 case 'M':
6598 delete_lines(1);
6599 goto got3;
6600
6601 case 'S':
6602 termcap_mode_start();
6603 goto got3;
6604
6605 case 'V':
6606 cursor_visible(TRUE);
6607 goto got3;
6608
6609 case 'v':
6610 cursor_visible(FALSE);
6611 goto got3;
6612
6613 got3:
6614 s += 3;
6615 len -= 2;
6616 }
6617
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006618# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00006619 if (fdDump)
6620 {
6621 fputs("ESC | ", fdDump);
6622 fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
6623 fputc('\n', fdDump);
6624 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006625# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006626 }
6627 else
6628 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006629 // Write a single character
Bram Moolenaar071d4272004-06-13 20:20:40 +00006630 DWORD nWritten;
6631
6632 nWritten = write_chars(s, 1);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006633# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00006634 if (fdDump)
6635 {
6636 fputc('>', fdDump);
6637 fwrite(s, sizeof(char_u), nWritten, fdDump);
6638 fputs("<\n", fdDump);
6639 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006640# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006641
6642 len -= (nWritten - 1);
6643 s += nWritten;
6644 }
6645 }
6646
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006647# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00006648 if (fdDump)
6649 fflush(fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006650# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006651}
6652
Bram Moolenaar0f873732019-12-05 20:28:46 +01006653#endif // FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00006654
6655
6656/*
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01006657 * Delay for "msec" milliseconds.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006658 */
6659 void
6660mch_delay(
6661 long msec,
Bram Moolenaar1266d672017-02-01 13:43:36 +01006662 int ignoreinput UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006663{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006664#if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01006665 Sleep((int)msec); // never wait for input
6666#else // Console
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006667# ifdef VIMDLL
6668 if (gui.in_use)
6669 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006670 Sleep((int)msec); // never wait for input
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006671 return;
6672 }
6673# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006674 if (ignoreinput)
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006675# ifdef FEAT_MZSCHEME
6676 if (mzthreads_allowed() && p_mzq > 0 && msec > p_mzq)
6677 {
6678 int towait = p_mzq;
6679
Bram Moolenaar0f873732019-12-05 20:28:46 +01006680 // if msec is large enough, wait by portions in p_mzq
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006681 while (msec > 0)
6682 {
6683 mzvim_check_threads();
6684 if (msec < towait)
6685 towait = msec;
6686 Sleep(towait);
6687 msec -= towait;
6688 }
6689 }
6690 else
6691# endif
6692 Sleep((int)msec);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006693 else
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006694 WaitForChar(msec, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006695#endif
6696}
6697
6698
6699/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01006700 * This version of remove is not scared by a readonly (backup) file.
6701 * This can also remove a symbolic link like Unix.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006702 * Return 0 for success, -1 for failure.
6703 */
6704 int
6705mch_remove(char_u *name)
6706{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006707 WCHAR *wn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006708 int n;
6709
Bram Moolenaar203258c2016-01-17 22:15:16 +01006710 /*
6711 * On Windows, deleting a directory's symbolic link is done by
6712 * RemoveDirectory(): mch_rmdir. It seems unnatural, but it is fact.
6713 */
6714 if (mch_isdir(name) && mch_is_symbolic_link(name))
6715 return mch_rmdir(name);
6716
Bram Moolenaar12b559e2013-06-12 22:41:37 +02006717 win32_setattrs(name, FILE_ATTRIBUTE_NORMAL);
6718
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006719 wn = enc_to_utf16(name, NULL);
6720 if (wn == NULL)
6721 return -1;
6722
6723 n = DeleteFileW(wn) ? 0 : -1;
6724 vim_free(wn);
6725 return n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006726}
6727
6728
6729/*
Bram Moolenaarb9c31e72016-09-29 15:18:57 +02006730 * Check for an "interrupt signal": CTRL-break or CTRL-C.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006731 */
6732 void
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02006733mch_breakcheck(int force UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006734{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006735#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
6736# ifdef VIMDLL
6737 if (!gui.in_use)
6738# endif
6739 if (g_fCtrlCPressed || g_fCBrkPressed)
6740 {
6741 ctrl_break_was_pressed = g_fCBrkPressed;
6742 g_fCtrlCPressed = g_fCBrkPressed = FALSE;
6743 got_int = TRUE;
6744 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006745#endif
6746}
6747
Bram Moolenaar0f873732019-12-05 20:28:46 +01006748// physical RAM to leave for the OS
Bram Moolenaaree273972016-01-02 21:11:51 +01006749#define WINNT_RESERVE_BYTES (256*1024*1024)
Bram Moolenaaree273972016-01-02 21:11:51 +01006750
6751/*
6752 * How much main memory in KiB that can be used by VIM.
6753 */
Bram Moolenaaree273972016-01-02 21:11:51 +01006754 long_u
Bram Moolenaar1266d672017-02-01 13:43:36 +01006755mch_total_mem(int special UNUSED)
Bram Moolenaaree273972016-01-02 21:11:51 +01006756{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006757 MEMORYSTATUSEX ms;
6758
Bram Moolenaar0f873732019-12-05 20:28:46 +01006759 // Need to use GlobalMemoryStatusEx() when there is more memory than
6760 // what fits in 32 bits.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006761 ms.dwLength = sizeof(MEMORYSTATUSEX);
6762 GlobalMemoryStatusEx(&ms);
6763 if (ms.ullAvailVirtual < ms.ullTotalPhys)
Bram Moolenaaree273972016-01-02 21:11:51 +01006764 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006765 // Process address space fits in physical RAM, use all of it.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006766 return (long_u)(ms.ullAvailVirtual / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01006767 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006768 if (ms.ullTotalPhys <= WINNT_RESERVE_BYTES)
Bram Moolenaaree273972016-01-02 21:11:51 +01006769 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006770 // Catch old NT box or perverse hardware setup.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006771 return (long_u)((ms.ullTotalPhys / 2) / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01006772 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01006773 // Use physical RAM less reserve for OS + data.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006774 return (long_u)((ms.ullTotalPhys - WINNT_RESERVE_BYTES) / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01006775}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006776
Bram Moolenaar071d4272004-06-13 20:20:40 +00006777/*
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006778 * mch_wrename() works around a bug in rename (aka MoveFile) in
Bram Moolenaar071d4272004-06-13 20:20:40 +00006779 * Windows 95: rename("foo.bar", "foo.bar~") will generate a
6780 * file whose short file name is "FOO.BAR" (its long file name will
6781 * be correct: "foo.bar~"). Because a file can be accessed by
6782 * either its SFN or its LFN, "foo.bar" has effectively been
6783 * renamed to "foo.bar", which is not at all what was wanted. This
6784 * seems to happen only when renaming files with three-character
6785 * extensions by appending a suffix that does not include ".".
6786 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
6787 *
6788 * There is another problem, which isn't really a bug but isn't right either:
6789 * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be
6790 * "abcdef~1.txt" again. This has been reported on Windows NT 4.0 with
6791 * service pack 6. Doesn't seem to happen on Windows 98.
6792 *
6793 * Like rename(), returns 0 upon success, non-zero upon failure.
6794 * Should probably set errno appropriately when errors occur.
6795 */
6796 int
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006797mch_wrename(WCHAR *wold, WCHAR *wnew)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006798{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006799 WCHAR *p;
6800 int i;
6801 WCHAR szTempFile[_MAX_PATH + 1];
6802 WCHAR szNewPath[_MAX_PATH + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006803 HANDLE hf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006804
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006805 // No need to play tricks unless the file name contains a "~" as the
6806 // seventh character.
6807 p = wold;
6808 for (i = 0; wold[i] != NUL; ++i)
6809 if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':')
6810 && wold[i + 1] != 0)
6811 p = wold + i + 1;
6812 if ((int)(wold + i - p) < 8 || p[6] != '~')
6813 return (MoveFileW(wold, wnew) == 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006814
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006815 // Get base path of new file name. Undocumented feature: If pszNewFile is
6816 // a directory, no error is returned and pszFilePart will be NULL.
6817 if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006818 return -1;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006819 *p = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006820
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006821 // Get (and create) a unique temporary file name in directory of new file
6822 if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006823 return -2;
6824
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006825 // blow the temp file away
6826 if (!DeleteFileW(szTempFile))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006827 return -3;
6828
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006829 // rename old file to the temp file
6830 if (!MoveFileW(wold, szTempFile))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006831 return -4;
6832
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006833 // now create an empty file called pszOldFile; this prevents the operating
6834 // system using pszOldFile as an alias (SFN) if we're renaming within the
6835 // same directory. For example, we're editing a file called
6836 // filename.asc.txt by its SFN, filena~1.txt. If we rename filena~1.txt
6837 // to filena~1.txt~ (i.e., we're making a backup while writing it), the
6838 // SFN for filena~1.txt~ will be filena~1.txt, by default, which will
6839 // cause all sorts of problems later in buf_write(). So, we create an
6840 // empty file called filena~1.txt and the system will have to find some
6841 // other SFN for filena~1.txt~, such as filena~2.txt
6842 if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006843 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
6844 return -5;
6845 if (!CloseHandle(hf))
6846 return -6;
6847
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006848 // rename the temp file to the new file
6849 if (!MoveFileW(szTempFile, wnew))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006850 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006851 // Renaming failed. Rename the file back to its old name, so that it
6852 // looks like nothing happened.
6853 (void)MoveFileW(szTempFile, wold);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006854 return -7;
6855 }
6856
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006857 // Seems to be left around on Novell filesystems
6858 DeleteFileW(szTempFile);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006859
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006860 // finally, remove the empty old file
6861 if (!DeleteFileW(wold))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006862 return -8;
6863
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006864 return 0;
6865}
6866
6867
6868/*
6869 * Converts the filenames to UTF-16, then call mch_wrename().
6870 * Like rename(), returns 0 upon success, non-zero upon failure.
6871 */
6872 int
6873mch_rename(
6874 const char *pszOldFile,
6875 const char *pszNewFile)
6876{
6877 WCHAR *wold = NULL;
6878 WCHAR *wnew = NULL;
6879 int retval = -1;
6880
6881 wold = enc_to_utf16((char_u *)pszOldFile, NULL);
6882 wnew = enc_to_utf16((char_u *)pszNewFile, NULL);
6883 if (wold != NULL && wnew != NULL)
6884 retval = mch_wrename(wold, wnew);
6885 vim_free(wold);
6886 vim_free(wnew);
6887 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006888}
6889
6890/*
6891 * Get the default shell for the current hardware platform
6892 */
6893 char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006894default_shell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006895{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006896 return "cmd.exe";
Bram Moolenaar071d4272004-06-13 20:20:40 +00006897}
6898
6899/*
6900 * mch_access() extends access() to do more detailed check on network drives.
6901 * Returns 0 if file "n" has access rights according to "p", -1 otherwise.
6902 */
6903 int
6904mch_access(char *n, int p)
6905{
6906 HANDLE hFile;
Bram Moolenaar0f873732019-12-05 20:28:46 +01006907 int retval = -1; // default: fail
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006908 WCHAR *wn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006909
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006910 wn = enc_to_utf16((char_u *)n, NULL);
6911 if (wn == NULL)
6912 return -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006913
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006914 if (mch_isdir((char_u *)n))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006915 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006916 WCHAR TempNameW[_MAX_PATH + 16] = L"";
Bram Moolenaar071d4272004-06-13 20:20:40 +00006917
6918 if (p & R_OK)
6919 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006920 // Read check is performed by seeing if we can do a find file on
6921 // the directory for any file.
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006922 int i;
6923 WIN32_FIND_DATAW d;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006924
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006925 for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i)
6926 TempNameW[i] = wn[i];
6927 if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/')
6928 TempNameW[i++] = '\\';
6929 TempNameW[i++] = '*';
6930 TempNameW[i++] = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006931
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006932 hFile = FindFirstFileW(TempNameW, &d);
6933 if (hFile == INVALID_HANDLE_VALUE)
6934 goto getout;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006935 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006936 (void)FindClose(hFile);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006937 }
6938
6939 if (p & W_OK)
6940 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006941 // Trying to create a temporary file in the directory should catch
6942 // directories on read-only network shares. However, in
6943 // directories whose ACL allows writes but denies deletes will end
6944 // up keeping the temporary file :-(.
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006945 if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW))
6946 goto getout;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006947 else
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006948 DeleteFileW(TempNameW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006949 }
6950 }
6951 else
6952 {
Bram Moolenaar5aa98962018-05-06 17:09:38 +02006953 // Don't consider a file read-only if another process has opened it.
6954 DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6955
Bram Moolenaar0f873732019-12-05 20:28:46 +01006956 // Trying to open the file for the required access does ACL, read-only
6957 // network share, and file attribute checks.
Bram Moolenaar5aa98962018-05-06 17:09:38 +02006958 DWORD access_mode = ((p & W_OK) ? GENERIC_WRITE : 0)
6959 | ((p & R_OK) ? GENERIC_READ : 0);
6960
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006961 hFile = CreateFileW(wn, access_mode, share_mode,
6962 NULL, OPEN_EXISTING, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006963 if (hFile == INVALID_HANDLE_VALUE)
6964 goto getout;
6965 CloseHandle(hFile);
6966 }
6967
Bram Moolenaar0f873732019-12-05 20:28:46 +01006968 retval = 0; // success
Bram Moolenaar071d4272004-06-13 20:20:40 +00006969getout:
Bram Moolenaar071d4272004-06-13 20:20:40 +00006970 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006971 return retval;
6972}
6973
Bram Moolenaar071d4272004-06-13 20:20:40 +00006974/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006975 * Version of open() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006976 */
6977 int
Bram Moolenaarb6843a02017-08-02 22:07:12 +02006978mch_open(const char *name, int flags, int mode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006979{
6980 WCHAR *wn;
6981 int f;
6982
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006983 wn = enc_to_utf16((char_u *)name, NULL);
6984 if (wn == NULL)
6985 return -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006986
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006987 f = _wopen(wn, flags, mode);
6988 vim_free(wn);
6989 return f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006990}
6991
6992/*
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006993 * Version of fopen() that uses UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006994 */
6995 FILE *
Bram Moolenaarb6843a02017-08-02 22:07:12 +02006996mch_fopen(const char *name, const char *mode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006997{
6998 WCHAR *wn, *wm;
6999 FILE *f = NULL;
7000
Bram Moolenaara12a1612019-01-24 16:39:02 +01007001#if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0f873732019-12-05 20:28:46 +01007002 // Work around an annoying assertion in the Microsoft debug CRT
7003 // when mode's text/binary setting doesn't match _get_fmode().
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007004 char newMode = mode[strlen(mode) - 1];
7005 int oldMode = 0;
Bram Moolenaar0fde2902008-03-16 13:54:13 +00007006
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007007 _get_fmode(&oldMode);
7008 if (newMode == 't')
7009 _set_fmode(_O_TEXT);
7010 else if (newMode == 'b')
7011 _set_fmode(_O_BINARY);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007012#endif
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007013 wn = enc_to_utf16((char_u *)name, NULL);
7014 wm = enc_to_utf16((char_u *)mode, NULL);
7015 if (wn != NULL && wm != NULL)
7016 f = _wfopen(wn, wm);
7017 vim_free(wn);
7018 vim_free(wm);
Bram Moolenaar0fde2902008-03-16 13:54:13 +00007019
Bram Moolenaara12a1612019-01-24 16:39:02 +01007020#if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007021 _set_fmode(oldMode);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007022#endif
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007023 return f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007024}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007025
Bram Moolenaar071d4272004-06-13 20:20:40 +00007026/*
7027 * SUB STREAM (aka info stream) handling:
7028 *
7029 * NTFS can have sub streams for each file. Normal contents of file is
7030 * stored in the main stream, and extra contents (author information and
7031 * title and so on) can be stored in sub stream. After Windows 2000, user
7032 * can access and store those informations in sub streams via explorer's
7033 * property menuitem in right click menu. Those informations in sub streams
7034 * were lost when copying only the main stream. So we have to copy sub
7035 * streams.
7036 *
7037 * Incomplete explanation:
7038 * http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp
7039 * More useful info and an example:
7040 * http://www.sysinternals.com/ntw2k/source/misc.shtml#streams
7041 */
7042
7043/*
7044 * Copy info stream data "substream". Read from the file with BackupRead(sh)
7045 * and write to stream "substream" of file "to".
7046 * Errors are ignored.
7047 */
7048 static void
7049copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len)
7050{
7051 HANDLE hTo;
7052 WCHAR *to_name;
7053
7054 to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR));
7055 wcscpy(to_name, to);
7056 wcscat(to_name, substream);
7057
7058 hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
7059 FILE_ATTRIBUTE_NORMAL, NULL);
7060 if (hTo != INVALID_HANDLE_VALUE)
7061 {
7062 long done;
7063 DWORD todo;
7064 DWORD readcnt, written;
7065 char buf[4096];
7066
Bram Moolenaar0f873732019-12-05 20:28:46 +01007067 // Copy block of bytes at a time. Abort when something goes wrong.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007068 for (done = 0; done < len; done += written)
7069 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007070 // (size_t) cast for Borland C 5.5
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007071 todo = (DWORD)((size_t)(len - done) > sizeof(buf) ? sizeof(buf)
7072 : (size_t)(len - done));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007073 if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt,
7074 FALSE, FALSE, context)
7075 || readcnt != todo
7076 || !WriteFile(hTo, buf, todo, &written, NULL)
7077 || written != todo)
7078 break;
7079 }
7080 CloseHandle(hTo);
7081 }
7082
7083 free(to_name);
7084}
7085
7086/*
7087 * Copy info streams from file "from" to file "to".
7088 */
7089 static void
7090copy_infostreams(char_u *from, char_u *to)
7091{
7092 WCHAR *fromw;
7093 WCHAR *tow;
7094 HANDLE sh;
7095 WIN32_STREAM_ID sid;
7096 int headersize;
7097 WCHAR streamname[_MAX_PATH];
7098 DWORD readcount;
7099 void *context = NULL;
7100 DWORD lo, hi;
7101 int len;
7102
Bram Moolenaar0f873732019-12-05 20:28:46 +01007103 // Convert the file names to wide characters.
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007104 fromw = enc_to_utf16(from, NULL);
7105 tow = enc_to_utf16(to, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007106 if (fromw != NULL && tow != NULL)
7107 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007108 // Open the file for reading.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007109 sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL,
7110 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
7111 if (sh != INVALID_HANDLE_VALUE)
7112 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007113 // Use BackupRead() to find the info streams. Repeat until we
7114 // have done them all.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007115 for (;;)
7116 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007117 // Get the header to find the length of the stream name. If
7118 // the "readcount" is zero we have done all info streams.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007119 ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007120 headersize = (int)((char *)&sid.cStreamName - (char *)&sid.dwStreamId);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007121 if (!BackupRead(sh, (LPBYTE)&sid, headersize,
7122 &readcount, FALSE, FALSE, &context)
7123 || readcount == 0)
7124 break;
7125
Bram Moolenaar0f873732019-12-05 20:28:46 +01007126 // We only deal with streams that have a name. The normal
7127 // file data appears to be without a name, even though docs
7128 // suggest it is called "::$DATA".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007129 if (sid.dwStreamNameSize > 0)
7130 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007131 // Read the stream name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007132 if (!BackupRead(sh, (LPBYTE)streamname,
7133 sid.dwStreamNameSize,
7134 &readcount, FALSE, FALSE, &context))
7135 break;
7136
Bram Moolenaar0f873732019-12-05 20:28:46 +01007137 // Copy an info stream with a name ":anything:$DATA".
7138 // Skip "::$DATA", it has no stream name (examples suggest
7139 // it might be used for the normal file contents).
7140 // Note that BackupRead() counts bytes, but the name is in
7141 // wide characters.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007142 len = readcount / sizeof(WCHAR);
7143 streamname[len] = 0;
7144 if (len > 7 && wcsicmp(streamname + len - 6,
7145 L":$DATA") == 0)
7146 {
7147 streamname[len - 6] = 0;
7148 copy_substream(sh, &context, tow, streamname,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00007149 (long)sid.Size.u.LowPart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007150 }
7151 }
7152
Bram Moolenaar0f873732019-12-05 20:28:46 +01007153 // Advance to the next stream. We might try seeking too far,
7154 // but BackupSeek() doesn't skip over stream borders, thus
7155 // that's OK.
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00007156 (void)BackupSeek(sh, sid.Size.u.LowPart, sid.Size.u.HighPart,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007157 &lo, &hi, &context);
7158 }
7159
Bram Moolenaar0f873732019-12-05 20:28:46 +01007160 // Clear the context.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007161 (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context);
7162
7163 CloseHandle(sh);
7164 }
7165 }
7166 vim_free(fromw);
7167 vim_free(tow);
7168}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007169
7170/*
7171 * Copy file attributes from file "from" to file "to".
7172 * For Windows NT and later we copy info streams.
7173 * Always returns zero, errors are ignored.
7174 */
7175 int
7176mch_copy_file_attribute(char_u *from, char_u *to)
7177{
Bram Moolenaar0f873732019-12-05 20:28:46 +01007178 // File streams only work on Windows NT and later.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007179 copy_infostreams(from, to);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007180 return 0;
7181}
7182
7183#if defined(MYRESETSTKOFLW) || defined(PROTO)
7184/*
7185 * Recreate a destroyed stack guard page in win32.
7186 * Written by Benjamin Peterson.
7187 */
7188
Bram Moolenaar0f873732019-12-05 20:28:46 +01007189// These magic numbers are from the MS header files
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007190# define MIN_STACK_WINNT 2
Bram Moolenaar071d4272004-06-13 20:20:40 +00007191
7192/*
7193 * This function does the same thing as _resetstkoflw(), which is only
7194 * available in DevStudio .net and later.
7195 * Returns 0 for failure, 1 for success.
7196 */
7197 int
7198myresetstkoflw(void)
7199{
7200 BYTE *pStackPtr;
7201 BYTE *pGuardPage;
7202 BYTE *pStackBase;
7203 BYTE *pLowestPossiblePage;
7204 MEMORY_BASIC_INFORMATION mbi;
7205 SYSTEM_INFO si;
7206 DWORD nPageSize;
7207 DWORD dummy;
7208
Bram Moolenaar0f873732019-12-05 20:28:46 +01007209 // We need to know the system page size.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007210 GetSystemInfo(&si);
7211 nPageSize = si.dwPageSize;
7212
Bram Moolenaar0f873732019-12-05 20:28:46 +01007213 // ...and the current stack pointer
Bram Moolenaar071d4272004-06-13 20:20:40 +00007214 pStackPtr = (BYTE*)_alloca(1);
7215
Bram Moolenaar0f873732019-12-05 20:28:46 +01007216 // ...and the base of the stack.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007217 if (VirtualQuery(pStackPtr, &mbi, sizeof mbi) == 0)
7218 return 0;
7219 pStackBase = (BYTE*)mbi.AllocationBase;
7220
Bram Moolenaar4b96df52020-01-26 22:00:26 +01007221 // ...and the page that's min_stack_req pages away from stack base; this is
Bram Moolenaar0f873732019-12-05 20:28:46 +01007222 // the lowest page we could use.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007223 pLowestPossiblePage = pStackBase + MIN_STACK_WINNT * nPageSize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007224
Bram Moolenaar071d4272004-06-13 20:20:40 +00007225 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007226 // We want the first committed page in the stack Start at the stack
7227 // base and move forward through memory until we find a committed block.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007228 BYTE *pBlock = pStackBase;
7229
Bram Moolenaara466c992005-07-09 21:03:22 +00007230 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007231 {
7232 if (VirtualQuery(pBlock, &mbi, sizeof mbi) == 0)
7233 return 0;
7234
7235 pBlock += mbi.RegionSize;
7236
7237 if (mbi.State & MEM_COMMIT)
7238 break;
7239 }
7240
Bram Moolenaar0f873732019-12-05 20:28:46 +01007241 // mbi now describes the first committed block in the stack.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007242 if (mbi.Protect & PAGE_GUARD)
7243 return 1;
7244
Bram Moolenaar0f873732019-12-05 20:28:46 +01007245 // decide where the guard page should start
Bram Moolenaar071d4272004-06-13 20:20:40 +00007246 if ((long_u)(mbi.BaseAddress) < (long_u)pLowestPossiblePage)
7247 pGuardPage = pLowestPossiblePage;
7248 else
7249 pGuardPage = (BYTE*)mbi.BaseAddress;
7250
Bram Moolenaar0f873732019-12-05 20:28:46 +01007251 // allocate the guard page
Bram Moolenaar071d4272004-06-13 20:20:40 +00007252 if (!VirtualAlloc(pGuardPage, nPageSize, MEM_COMMIT, PAGE_READWRITE))
7253 return 0;
7254
Bram Moolenaar0f873732019-12-05 20:28:46 +01007255 // apply the guard attribute to the page
Bram Moolenaar071d4272004-06-13 20:20:40 +00007256 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_READWRITE | PAGE_GUARD,
7257 &dummy))
7258 return 0;
7259 }
7260
7261 return 1;
7262}
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007263#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007264
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007265
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007266/*
7267 * The command line arguments in UCS2
7268 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00007269static int nArgsW = 0;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007270static LPWSTR *ArglistW = NULL;
7271static int global_argc = 0;
7272static char **global_argv;
7273
Bram Moolenaar0f873732019-12-05 20:28:46 +01007274static int used_file_argc = 0; // last argument in global_argv[] used
7275 // for the argument list.
7276static int *used_file_indexes = NULL; // indexes in global_argv[] for
7277 // command line arguments added to
7278 // the argument list
7279static int used_file_count = 0; // nr of entries in used_file_indexes
7280static int used_file_literal = FALSE; // take file names literally
7281static int used_file_full_path = FALSE; // file name was full path
7282static int used_file_diff_mode = FALSE; // file name was with diff mode
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007283static int used_alist_count = 0;
7284
7285
7286/*
7287 * Get the command line arguments. Unicode version.
7288 * Returns argc. Zero when something fails.
7289 */
7290 int
7291get_cmd_argsW(char ***argvp)
7292{
7293 char **argv = NULL;
7294 int argc = 0;
7295 int i;
7296
Bram Moolenaar14993322014-09-09 12:25:33 +02007297 free_cmd_argsW();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007298 ArglistW = CommandLineToArgvW(GetCommandLineW(), &nArgsW);
7299 if (ArglistW != NULL)
7300 {
7301 argv = malloc((nArgsW + 1) * sizeof(char *));
7302 if (argv != NULL)
7303 {
7304 argc = nArgsW;
7305 argv[argc] = NULL;
7306 for (i = 0; i < argc; ++i)
7307 {
7308 int len;
7309
Bram Moolenaar0f873732019-12-05 20:28:46 +01007310 // Convert each Unicode argument to the current codepage.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007311 WideCharToMultiByte_alloc(GetACP(), 0,
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007312 ArglistW[i], (int)wcslen(ArglistW[i]) + 1,
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007313 (LPSTR *)&argv[i], &len, 0, 0);
7314 if (argv[i] == NULL)
7315 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007316 // Out of memory, clear everything.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007317 while (i > 0)
7318 free(argv[--i]);
7319 free(argv);
Bram Moolenaar73c61632013-12-07 14:48:10 +01007320 argv = NULL;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007321 argc = 0;
7322 }
7323 }
7324 }
7325 }
7326
7327 global_argc = argc;
7328 global_argv = argv;
7329 if (argc > 0)
Bram Moolenaar14993322014-09-09 12:25:33 +02007330 {
7331 if (used_file_indexes != NULL)
7332 free(used_file_indexes);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007333 used_file_indexes = malloc(argc * sizeof(int));
Bram Moolenaar14993322014-09-09 12:25:33 +02007334 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007335
7336 if (argvp != NULL)
7337 *argvp = argv;
7338 return argc;
7339}
7340
7341 void
7342free_cmd_argsW(void)
7343{
7344 if (ArglistW != NULL)
7345 {
7346 GlobalFree(ArglistW);
7347 ArglistW = NULL;
7348 }
7349}
7350
7351/*
7352 * Remember "name" is an argument that was added to the argument list.
7353 * This avoids that we have to re-parse the argument list when fix_arg_enc()
7354 * is called.
7355 */
7356 void
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007357used_file_arg(char *name, int literal, int full_path, int diff_mode)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007358{
7359 int i;
7360
7361 if (used_file_indexes == NULL)
7362 return;
7363 for (i = used_file_argc + 1; i < global_argc; ++i)
7364 if (STRCMP(global_argv[i], name) == 0)
7365 {
7366 used_file_argc = i;
7367 used_file_indexes[used_file_count++] = i;
7368 break;
7369 }
7370 used_file_literal = literal;
7371 used_file_full_path = full_path;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007372 used_file_diff_mode = diff_mode;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007373}
7374
7375/*
7376 * Remember the length of the argument list as it was. If it changes then we
7377 * leave it alone when 'encoding' is set.
7378 */
7379 void
7380set_alist_count(void)
7381{
7382 used_alist_count = GARGCOUNT;
7383}
7384
7385/*
7386 * Fix the encoding of the command line arguments. Invoked when 'encoding'
7387 * has been changed while starting up. Use the UCS-2 command line arguments
7388 * and convert them to 'encoding'.
7389 */
7390 void
7391fix_arg_enc(void)
7392{
7393 int i;
7394 int idx;
7395 char_u *str;
Bram Moolenaar86b68352004-12-27 21:59:20 +00007396 int *fnum_list;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007397
Bram Moolenaar0f873732019-12-05 20:28:46 +01007398 // Safety checks:
7399 // - if argument count differs between the wide and non-wide argument
7400 // list, something must be wrong.
7401 // - the file name arguments must have been located.
7402 // - the length of the argument list wasn't changed by the user.
Bram Moolenaard857f0e2005-06-21 22:37:39 +00007403 if (global_argc != nArgsW
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007404 || ArglistW == NULL
7405 || used_file_indexes == NULL
7406 || used_file_count == 0
7407 || used_alist_count != GARGCOUNT)
7408 return;
7409
Bram Moolenaar0f873732019-12-05 20:28:46 +01007410 // Remember the buffer numbers for the arguments.
Bram Moolenaarc799fe22019-05-28 23:08:19 +02007411 fnum_list = ALLOC_MULT(int, GARGCOUNT);
Bram Moolenaar86b68352004-12-27 21:59:20 +00007412 if (fnum_list == NULL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007413 return; // out of memory
Bram Moolenaar86b68352004-12-27 21:59:20 +00007414 for (i = 0; i < GARGCOUNT; ++i)
7415 fnum_list[i] = GARGLIST[i].ae_fnum;
7416
Bram Moolenaar0f873732019-12-05 20:28:46 +01007417 // Clear the argument list. Make room for the new arguments.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007418 alist_clear(&global_alist);
7419 if (ga_grow(&global_alist.al_ga, used_file_count) == FAIL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007420 return; // out of memory
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007421
7422 for (i = 0; i < used_file_count; ++i)
7423 {
7424 idx = used_file_indexes[i];
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007425 str = utf16_to_enc(ArglistW[idx], NULL);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007426 if (str != NULL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00007427 {
Bram Moolenaar39d21e32017-08-05 23:09:31 +02007428 int literal = used_file_literal;
7429
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007430#ifdef FEAT_DIFF
Bram Moolenaar0f873732019-12-05 20:28:46 +01007431 // When using diff mode may need to concatenate file name to
7432 // directory name. Just like it's done in main().
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007433 if (used_file_diff_mode && mch_isdir(str) && GARGCOUNT > 0
7434 && !mch_isdir(alist_name(&GARGLIST[0])))
7435 {
7436 char_u *r;
7437
7438 r = concat_fnames(str, gettail(alist_name(&GARGLIST[0])), TRUE);
7439 if (r != NULL)
7440 {
7441 vim_free(str);
7442 str = r;
7443 }
7444 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007445#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01007446 // Re-use the old buffer by renaming it. When not using literal
7447 // names it's done by alist_expand() below.
Bram Moolenaar86b68352004-12-27 21:59:20 +00007448 if (used_file_literal)
7449 buf_set_name(fnum_list[i], str);
7450
Bram Moolenaar0f873732019-12-05 20:28:46 +01007451 // Check backtick literal. backtick literal is already expanded in
7452 // main.c, so this part add str as literal.
Bram Moolenaar39d21e32017-08-05 23:09:31 +02007453 if (literal == FALSE)
7454 {
Bram Moolenaar116a0f82017-08-07 21:17:57 +02007455 size_t len = STRLEN(str);
7456
Bram Moolenaar39d21e32017-08-05 23:09:31 +02007457 if (len > 2 && *str == '`' && *(str + len - 1) == '`')
7458 literal = TRUE;
7459 }
7460 alist_add(&global_alist, str, literal ? 2 : 0);
Bram Moolenaar86b68352004-12-27 21:59:20 +00007461 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007462 }
7463
7464 if (!used_file_literal)
7465 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007466 // Now expand wildcards in the arguments.
7467 // Temporarily add '(' and ')' to 'isfname'. These are valid
7468 // filename characters but are excluded from 'isfname' to make
7469 // "gf" work on a file name in parenthesis (e.g.: see vim.h).
7470 // Also, unset wildignore to not be influenced by this option.
7471 // The arguments specified in command-line should be kept even if
7472 // encoding options were changed.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007473 do_cmdline_cmd((char_u *)":let SaVe_ISF = &isf|set isf+=(,)");
Bram Moolenaar20586cb2018-03-08 22:03:14 +01007474 do_cmdline_cmd((char_u *)":let SaVe_WIG = &wig|set wig=");
Bram Moolenaar86b68352004-12-27 21:59:20 +00007475 alist_expand(fnum_list, used_alist_count);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007476 do_cmdline_cmd((char_u *)":let &isf = SaVe_ISF|unlet SaVe_ISF");
Bram Moolenaar20586cb2018-03-08 22:03:14 +01007477 do_cmdline_cmd((char_u *)":let &wig = SaVe_WIG|unlet SaVe_WIG");
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007478 }
7479
Bram Moolenaar0f873732019-12-05 20:28:46 +01007480 // If wildcard expansion failed, we are editing the first file of the
7481 // arglist and there is no file name: Edit the first argument now.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007482 if (curwin->w_arg_idx == 0 && curbuf->b_fname == NULL)
7483 {
7484 do_cmdline_cmd((char_u *)":rewind");
7485 if (GARGCOUNT == 1 && used_file_full_path)
Bram Moolenaarb7407d32018-02-03 17:36:27 +01007486 (void)vim_chdirfile(alist_name(&GARGLIST[0]), "drop");
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007487 }
Bram Moolenaar86b68352004-12-27 21:59:20 +00007488
7489 set_alist_count();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007490}
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007491
7492 int
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02007493mch_setenv(char *var, char *value, int x UNUSED)
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007494{
7495 char_u *envbuf;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007496 WCHAR *p;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007497
Bram Moolenaar964b3742019-05-24 18:54:09 +02007498 envbuf = alloc(STRLEN(var) + STRLEN(value) + 2);
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007499 if (envbuf == NULL)
7500 return -1;
7501
7502 sprintf((char *)envbuf, "%s=%s", var, value);
7503
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007504 p = enc_to_utf16(envbuf, NULL);
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007505
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007506 vim_free(envbuf);
7507 if (p == NULL)
7508 return -1;
7509 _wputenv(p);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007510#ifdef libintl_wputenv
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007511 libintl_wputenv(p);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007512#endif
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007513 // Unlike Un*x systems, we can free the string for _wputenv().
7514 vim_free(p);
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01007515
7516 return 0;
7517}
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007518
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007519/*
7520 * Support for 256 colors and 24-bit colors was added in Windows 10
7521 * version 1703 (Creators update).
7522 */
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007523#define VTP_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 15063)
7524
7525/*
7526 * Support for pseudo-console (ConPTY) was added in windows 10
Bram Moolenaar57da6982019-09-13 22:30:11 +02007527 * version 1809 (October 2018 update).
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007528 */
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01007529#define CONPTY_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 17763)
Bram Moolenaar57da6982019-09-13 22:30:11 +02007530
7531/*
7532 * ConPTY differences between versions, need different logic.
7533 * version 1903 (May 2019 update).
7534 */
7535#define CONPTY_1903_BUILD MAKE_VER(10, 0, 18362)
7536
7537/*
Bram Moolenaar36e7a822019-11-13 21:49:24 +01007538 * version 1909 (November 2019 update).
7539 */
7540#define CONPTY_1909_BUILD MAKE_VER(10, 0, 18363)
7541
7542/*
Bram Moolenaar7ed8f592020-04-28 20:44:42 +02007543 * Stay ahead of the next update, and when it's done, fix this.
7544 * version ? (2020 update, temporarily use the build number of insider preview)
7545 */
7546#define CONPTY_NEXT_UPDATE_BUILD MAKE_VER(10, 0, 19587)
7547
7548/*
Bram Moolenaar57da6982019-09-13 22:30:11 +02007549 * Confirm until this version. Also the logic changes.
7550 * insider preview.
7551 */
Bram Moolenaar4c063dd2019-10-04 21:29:12 +02007552#define CONPTY_INSIDER_BUILD MAKE_VER(10, 0, 18995)
Bram Moolenaar57da6982019-09-13 22:30:11 +02007553
7554/*
7555 * Not stable now.
7556 */
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01007557#define CONPTY_STABLE_BUILD MAKE_VER(10, 0, 32767) // T.B.D.
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007558
7559 static void
7560vtp_flag_init(void)
7561{
7562 DWORD ver = get_build_number();
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007563#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007564 DWORD mode;
7565 HANDLE out;
7566
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007567# ifdef VIMDLL
7568 if (!gui.in_use)
7569# endif
7570 {
7571 out = GetStdHandle(STD_OUTPUT_HANDLE);
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007572
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007573 vtp_working = (ver >= VTP_FIRST_SUPPORT_BUILD) ? 1 : 0;
7574 GetConsoleMode(out, &mode);
7575 mode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
7576 if (SetConsoleMode(out, mode) == 0)
7577 vtp_working = 0;
7578 }
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007579#endif
7580
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007581 if (ver >= CONPTY_FIRST_SUPPORT_BUILD)
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01007582 conpty_working = 1;
7583 if (ver >= CONPTY_STABLE_BUILD)
7584 conpty_stable = 1;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007585
Bram Moolenaar57da6982019-09-13 22:30:11 +02007586 if (ver <= CONPTY_INSIDER_BUILD)
7587 conpty_type = 3;
Bram Moolenaar36e7a822019-11-13 21:49:24 +01007588 if (ver <= CONPTY_1909_BUILD)
7589 conpty_type = 2;
Bram Moolenaar57da6982019-09-13 22:30:11 +02007590 if (ver <= CONPTY_1903_BUILD)
7591 conpty_type = 2;
7592 if (ver < CONPTY_FIRST_SUPPORT_BUILD)
7593 conpty_type = 1;
Bram Moolenaar7ed8f592020-04-28 20:44:42 +02007594
7595 if (ver >= CONPTY_NEXT_UPDATE_BUILD)
7596 conpty_fix_type = 1;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01007597}
7598
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007599#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) || defined(PROTO)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007600
7601 static void
7602vtp_init(void)
7603{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007604 HMODULE hKerneldll;
7605 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi;
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007606# ifdef FEAT_TERMGUICOLORS
7607 COLORREF fg, bg;
7608# endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007609
Bram Moolenaar0f873732019-12-05 20:28:46 +01007610 // Use functions supported from Vista
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007611 hKerneldll = GetModuleHandle("kernel32.dll");
7612 if (hKerneldll != NULL)
7613 {
7614 pGetConsoleScreenBufferInfoEx =
7615 (PfnGetConsoleScreenBufferInfoEx)GetProcAddress(
7616 hKerneldll, "GetConsoleScreenBufferInfoEx");
7617 pSetConsoleScreenBufferInfoEx =
7618 (PfnSetConsoleScreenBufferInfoEx)GetProcAddress(
7619 hKerneldll, "SetConsoleScreenBufferInfoEx");
7620 if (pGetConsoleScreenBufferInfoEx != NULL
7621 && pSetConsoleScreenBufferInfoEx != NULL)
7622 has_csbiex = TRUE;
7623 }
7624
7625 csbi.cbSize = sizeof(csbi);
7626 if (has_csbiex)
7627 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007628 save_console_bg_rgb = (guicolor_T)csbi.ColorTable[g_color_index_bg];
7629 save_console_fg_rgb = (guicolor_T)csbi.ColorTable[g_color_index_fg];
Bram Moolenaardf543822020-01-30 11:53:59 +01007630 store_console_bg_rgb = save_console_bg_rgb;
7631 store_console_fg_rgb = save_console_fg_rgb;
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007632
7633# ifdef FEAT_TERMGUICOLORS
7634 bg = (COLORREF)csbi.ColorTable[g_color_index_bg];
7635 fg = (COLORREF)csbi.ColorTable[g_color_index_fg];
7636 bg = (GetRValue(bg) << 16) | (GetGValue(bg) << 8) | GetBValue(bg);
7637 fg = (GetRValue(fg) << 16) | (GetGValue(fg) << 8) | GetBValue(fg);
7638 default_console_color_bg = bg;
7639 default_console_color_fg = fg;
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01007640# endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007641
7642 set_console_color_rgb();
7643}
7644
7645 static void
7646vtp_exit(void)
7647{
Bram Moolenaardf543822020-01-30 11:53:59 +01007648 restore_console_color_rgb();
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007649}
7650
Bram Moolenaar06b7b582020-05-30 17:49:25 +02007651 int
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007652vtp_printf(
7653 char *format,
7654 ...)
7655{
7656 char_u buf[100];
7657 va_list list;
7658 DWORD result;
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007659 int len;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007660
7661 va_start(list, format);
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007662 len = vim_vsnprintf((char *)buf, 100, (char *)format, list);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007663 va_end(list);
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007664 WriteConsoleA(g_hConOut, buf, (DWORD)len, &result, NULL);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007665 return (int)result;
7666}
7667
7668 static void
7669vtp_sgr_bulk(
7670 int arg)
7671{
7672 int args[1];
7673
7674 args[0] = arg;
7675 vtp_sgr_bulks(1, args);
7676}
7677
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007678#define FAST256(x) \
7679 if ((*p-- = "0123456789"[(n = x % 10)]) \
7680 && x >= 10 && (*p-- = "0123456789"[((m = x % 100) - n) / 10]) \
7681 && x >= 100 && (*p-- = "012"[((x & 0xff) - m) / 100]));
7682
7683#define FAST256CASE(x) \
7684 case x: \
7685 FAST256(newargs[x - 1]);
7686
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007687 static void
7688vtp_sgr_bulks(
7689 int argc,
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007690 int *args)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007691{
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007692#define MAXSGR 16
7693#define SGRBUFSIZE 2 + 4 * MAXSGR + 1 // '\033[' + SGR + 'm'
7694 char_u buf[SGRBUFSIZE];
7695 char_u *p;
7696 int in, out;
7697 int newargs[16];
7698 static int sgrfgr = -1, sgrfgg, sgrfgb;
7699 static int sgrbgr = -1, sgrbgg, sgrbgb;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007700
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007701 if (argc == 0)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007702 {
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007703 sgrfgr = sgrbgr = -1;
7704 vtp_printf("033[m");
7705 return;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007706 }
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007707
7708 in = out = 0;
7709 while (in < argc)
7710 {
7711 int s = args[in];
7712 int copylen = 1;
7713
7714 if (s == 38)
7715 {
7716 if (argc - in >= 5 && args[in + 1] == 2)
7717 {
7718 if (sgrfgr == args[in + 2] && sgrfgg == args[in + 3]
7719 && sgrfgb == args[in + 4])
7720 {
7721 in += 5;
7722 copylen = 0;
7723 }
7724 else
7725 {
7726 sgrfgr = args[in + 2];
7727 sgrfgg = args[in + 3];
7728 sgrfgb = args[in + 4];
7729 copylen = 5;
7730 }
7731 }
7732 else if (argc - in >= 3 && args[in + 1] == 5)
7733 {
7734 sgrfgr = -1;
7735 copylen = 3;
7736 }
7737 }
7738 else if (s == 48)
7739 {
7740 if (argc - in >= 5 && args[in + 1] == 2)
7741 {
7742 if (sgrbgr == args[in + 2] && sgrbgg == args[in + 3]
7743 && sgrbgb == args[in + 4])
7744 {
7745 in += 5;
7746 copylen = 0;
7747 }
7748 else
7749 {
7750 sgrbgr = args[in + 2];
7751 sgrbgg = args[in + 3];
7752 sgrbgb = args[in + 4];
7753 copylen = 5;
7754 }
7755 }
7756 else if (argc - in >= 3 && args[in + 1] == 5)
7757 {
7758 sgrbgr = -1;
7759 copylen = 3;
7760 }
7761 }
7762 else if (30 <= s && s <= 39)
7763 sgrfgr = -1;
7764 else if (90 <= s && s <= 97)
7765 sgrfgr = -1;
7766 else if (40 <= s && s <= 49)
7767 sgrbgr = -1;
7768 else if (100 <= s && s <= 107)
7769 sgrbgr = -1;
7770 else if (s == 0)
7771 sgrfgr = sgrbgr = -1;
7772
7773 while (copylen--)
7774 newargs[out++] = args[in++];
7775 }
7776
7777 p = &buf[sizeof(buf) - 1];
7778 *p-- = 'm';
7779
7780 switch (out)
7781 {
7782 int n, m;
7783 DWORD r;
7784
7785 FAST256CASE(16);
7786 *p-- = ';';
7787 FAST256CASE(15);
7788 *p-- = ';';
7789 FAST256CASE(14);
7790 *p-- = ';';
7791 FAST256CASE(13);
7792 *p-- = ';';
7793 FAST256CASE(12);
7794 *p-- = ';';
7795 FAST256CASE(11);
7796 *p-- = ';';
7797 FAST256CASE(10);
7798 *p-- = ';';
7799 FAST256CASE(9);
7800 *p-- = ';';
7801 FAST256CASE(8);
7802 *p-- = ';';
7803 FAST256CASE(7);
7804 *p-- = ';';
7805 FAST256CASE(6);
7806 *p-- = ';';
7807 FAST256CASE(5);
7808 *p-- = ';';
7809 FAST256CASE(4);
7810 *p-- = ';';
7811 FAST256CASE(3);
7812 *p-- = ';';
7813 FAST256CASE(2);
7814 *p-- = ';';
7815 FAST256CASE(1);
7816 *p-- = '[';
7817 *p = '\033';
7818 WriteConsoleA(g_hConOut, p, (DWORD)(&buf[SGRBUFSIZE] - p), &r, NULL);
7819 default:
7820 break;
7821 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007822}
7823
Bram Moolenaar06b7b582020-05-30 17:49:25 +02007824 static void
7825wt_init(void)
7826{
7827 wt_working = (mch_getenv("WT_SESSION") != NULL);
7828}
7829
7830 int
7831use_wt(void)
7832{
7833 return USE_WT;
7834}
7835
Bram Moolenaar8a938af2018-05-01 17:30:41 +02007836# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007837 static int
7838ctermtoxterm(
7839 int cterm)
7840{
Bram Moolenaar9894e392018-05-05 14:29:06 +02007841 char_u r, g, b, idx;
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007842
7843 cterm_color2rgb(cterm, &r, &g, &b, &idx);
7844 return (((int)r << 16) | ((int)g << 8) | (int)b);
7845}
Bram Moolenaar8a938af2018-05-01 17:30:41 +02007846# endif
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007847
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007848 static void
7849set_console_color_rgb(void)
7850{
7851# ifdef FEAT_TERMGUICOLORS
7852 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi;
Bram Moolenaara050b942019-12-02 21:35:31 +01007853 guicolor_T fg, bg;
7854 int ctermfg, ctermbg;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007855
7856 if (!USE_VTP)
7857 return;
7858
Bram Moolenaara050b942019-12-02 21:35:31 +01007859 get_default_console_color(&ctermfg, &ctermbg, &fg, &bg);
7860
Bram Moolenaar06b7b582020-05-30 17:49:25 +02007861 if (USE_WT)
7862 {
7863 term_fg_rgb_color(fg);
7864 term_bg_rgb_color(bg);
7865 return;
7866 }
7867
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007868 fg = (GetRValue(fg) << 16) | (GetGValue(fg) << 8) | GetBValue(fg);
7869 bg = (GetRValue(bg) << 16) | (GetGValue(bg) << 8) | GetBValue(bg);
7870
7871 csbi.cbSize = sizeof(csbi);
7872 if (has_csbiex)
7873 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7874
7875 csbi.cbSize = sizeof(csbi);
7876 csbi.srWindow.Right += 1;
7877 csbi.srWindow.Bottom += 1;
Bram Moolenaardf543822020-01-30 11:53:59 +01007878 store_console_bg_rgb = csbi.ColorTable[g_color_index_bg];
7879 store_console_fg_rgb = csbi.ColorTable[g_color_index_fg];
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007880 csbi.ColorTable[g_color_index_bg] = (COLORREF)bg;
7881 csbi.ColorTable[g_color_index_fg] = (COLORREF)fg;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007882 if (has_csbiex)
7883 pSetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7884# endif
7885}
7886
Bram Moolenaara050b942019-12-02 21:35:31 +01007887# if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
7888 void
7889get_default_console_color(
7890 int *cterm_fg,
7891 int *cterm_bg,
7892 guicolor_T *gui_fg,
7893 guicolor_T *gui_bg)
7894{
7895 int id;
7896 guicolor_T guifg = INVALCOLOR;
7897 guicolor_T guibg = INVALCOLOR;
7898 int ctermfg = 0;
7899 int ctermbg = 0;
7900
7901 id = syn_name2id((char_u *)"Normal");
7902 if (id > 0 && p_tgc)
7903 syn_id2colors(id, &guifg, &guibg);
7904 if (guifg == INVALCOLOR)
7905 {
7906 ctermfg = -1;
7907 if (id > 0)
7908 syn_id2cterm_bg(id, &ctermfg, &ctermbg);
7909 guifg = ctermfg != -1 ? ctermtoxterm(ctermfg)
7910 : default_console_color_fg;
7911 cterm_normal_fg_gui_color = guifg;
7912 ctermfg = ctermfg < 0 ? 0 : ctermfg;
7913 }
7914 if (guibg == INVALCOLOR)
7915 {
7916 ctermbg = -1;
7917 if (id > 0)
7918 syn_id2cterm_bg(id, &ctermfg, &ctermbg);
7919 guibg = ctermbg != -1 ? ctermtoxterm(ctermbg)
7920 : default_console_color_bg;
7921 cterm_normal_bg_gui_color = guibg;
7922 ctermbg = ctermbg < 0 ? 0 : ctermbg;
7923 }
7924
7925 *cterm_fg = ctermfg;
7926 *cterm_bg = ctermbg;
7927 *gui_fg = guifg;
7928 *gui_bg = guibg;
7929}
7930# endif
7931
Bram Moolenaardf543822020-01-30 11:53:59 +01007932/*
7933 * Set the console colors to the original colors or the last set colors.
7934 */
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007935 static void
7936reset_console_color_rgb(void)
7937{
7938# ifdef FEAT_TERMGUICOLORS
7939 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi;
7940
Bram Moolenaar06b7b582020-05-30 17:49:25 +02007941 if (USE_WT)
7942 return;
7943
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007944 csbi.cbSize = sizeof(csbi);
7945 if (has_csbiex)
7946 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7947
7948 csbi.cbSize = sizeof(csbi);
7949 csbi.srWindow.Right += 1;
7950 csbi.srWindow.Bottom += 1;
Bram Moolenaardf543822020-01-30 11:53:59 +01007951 csbi.ColorTable[g_color_index_bg] = (COLORREF)store_console_bg_rgb;
7952 csbi.ColorTable[g_color_index_fg] = (COLORREF)store_console_fg_rgb;
7953 if (has_csbiex)
7954 pSetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7955# endif
7956}
7957
7958/*
7959 * Set the console colors to the original colors.
7960 */
7961 static void
7962restore_console_color_rgb(void)
7963{
7964# ifdef FEAT_TERMGUICOLORS
7965 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi;
7966
7967 csbi.cbSize = sizeof(csbi);
7968 if (has_csbiex)
7969 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7970
7971 csbi.cbSize = sizeof(csbi);
7972 csbi.srWindow.Right += 1;
7973 csbi.srWindow.Bottom += 1;
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02007974 csbi.ColorTable[g_color_index_bg] = (COLORREF)save_console_bg_rgb;
7975 csbi.ColorTable[g_color_index_fg] = (COLORREF)save_console_fg_rgb;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007976 if (has_csbiex)
7977 pSetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
7978# endif
7979}
7980
7981 void
7982control_console_color_rgb(void)
7983{
7984 if (USE_VTP)
7985 set_console_color_rgb();
7986 else
7987 reset_console_color_rgb();
7988}
7989
7990 int
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007991use_vtp(void)
7992{
7993 return USE_VTP;
7994}
7995
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02007996 int
7997is_term_win32(void)
7998{
7999 return T_NAME != NULL && STRCMP(T_NAME, "win32") == 0;
8000}
8001
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01008002 int
8003has_vtp_working(void)
8004{
8005 return vtp_working;
8006}
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01008007
Bram Moolenaar6902c0e2019-02-16 14:07:37 +01008008#endif
8009
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01008010 int
8011has_conpty_working(void)
8012{
8013 return conpty_working;
8014}
8015
8016 int
Bram Moolenaar57da6982019-09-13 22:30:11 +02008017get_conpty_type(void)
8018{
8019 return conpty_type;
8020}
8021
8022 int
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01008023is_conpty_stable(void)
8024{
8025 return conpty_stable;
8026}
Bram Moolenaar78d21da2019-02-17 15:00:52 +01008027
Bram Moolenaar7ed8f592020-04-28 20:44:42 +02008028 int
8029get_conpty_fix_type(void)
8030{
8031 return conpty_fix_type;
8032}
8033
Bram Moolenaarafde13b2019-04-28 19:46:49 +02008034#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) || defined(PROTO)
Bram Moolenaar78d21da2019-02-17 15:00:52 +01008035 void
8036resize_console_buf(void)
8037{
8038 CONSOLE_SCREEN_BUFFER_INFO csbi;
8039 COORD coord;
8040 SMALL_RECT newsize;
8041
8042 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
8043 {
8044 coord.X = SRWIDTH(csbi.srWindow);
8045 coord.Y = SRHEIGHT(csbi.srWindow);
8046 SetConsoleScreenBufferSize(g_hConOut, coord);
8047
8048 newsize.Left = 0;
8049 newsize.Top = 0;
8050 newsize.Right = coord.X - 1;
8051 newsize.Bottom = coord.Y - 1;
8052 SetConsoleWindowInfo(g_hConOut, TRUE, &newsize);
8053
8054 SetConsoleScreenBufferSize(g_hConOut, coord);
8055 }
8056}
8057#endif