blob: 5db5418903b130fefe7bdf2f0edbd86d56f6477d [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9/*
10 * os_win32.c
11 *
12 * Used for both the console version and the Win32 GUI. A lot of code is for
13 * the console version only, so there is a lot of "#ifndef FEAT_GUI_W32".
14 *
15 * Win32 (Windows NT and Windows 95) system-dependent routines.
16 * Portions lifted from the Win32 SDK samples, the MSDOS-dependent code,
17 * NetHack 3.1.3, GNU Emacs 19.30, and Vile 5.5.
18 *
19 * George V. Reilly <george@reilly.org> wrote most of this.
20 * Roger Knobbe <rogerk@wonderware.com> did the initial port of Vim 3.0.
21 */
22
Bram Moolenaar071d4272004-06-13 20:20:40 +000023#include "vim.h"
24
Bram Moolenaar325b7a22004-07-05 15:58:32 +000025#ifdef FEAT_MZSCHEME
26# include "if_mzsch.h"
27#endif
28
Bram Moolenaar071d4272004-06-13 20:20:40 +000029#include <sys/types.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000030#include <signal.h>
31#include <limits.h>
Bram Moolenaar82881492012-11-20 16:53:39 +010032
33/* cproto fails on missing include files */
34#ifndef PROTO
35# include <process.h>
36#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000037
38#undef chdir
39#ifdef __GNUC__
40# ifndef __MINGW32__
41# include <dirent.h>
42# endif
43#else
44# include <direct.h>
45#endif
46
Bram Moolenaar82881492012-11-20 16:53:39 +010047#ifndef PROTO
48# if defined(FEAT_TITLE) && !defined(FEAT_GUI_W32)
49# include <shellapi.h>
50# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000051#endif
52
53#ifdef __MINGW32__
54# ifndef FROM_LEFT_1ST_BUTTON_PRESSED
55# define FROM_LEFT_1ST_BUTTON_PRESSED 0x0001
56# endif
57# ifndef RIGHTMOST_BUTTON_PRESSED
58# define RIGHTMOST_BUTTON_PRESSED 0x0002
59# endif
60# ifndef FROM_LEFT_2ND_BUTTON_PRESSED
61# define FROM_LEFT_2ND_BUTTON_PRESSED 0x0004
62# endif
63# ifndef FROM_LEFT_3RD_BUTTON_PRESSED
64# define FROM_LEFT_3RD_BUTTON_PRESSED 0x0008
65# endif
66# ifndef FROM_LEFT_4TH_BUTTON_PRESSED
67# define FROM_LEFT_4TH_BUTTON_PRESSED 0x0010
68# endif
69
70/*
71 * EventFlags
72 */
73# ifndef MOUSE_MOVED
74# define MOUSE_MOVED 0x0001
75# endif
76# ifndef DOUBLE_CLICK
77# define DOUBLE_CLICK 0x0002
78# endif
79#endif
80
81/* Record all output and all keyboard & mouse input */
82/* #define MCH_WRITE_DUMP */
83
84#ifdef MCH_WRITE_DUMP
85FILE* fdDump = NULL;
86#endif
87
88/*
89 * When generating prototypes for Win32 on Unix, these lines make the syntax
90 * errors disappear. They do not need to be correct.
91 */
92#ifdef PROTO
93#define WINAPI
Bram Moolenaar071d4272004-06-13 20:20:40 +000094typedef char * LPCSTR;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +000095typedef char * LPWSTR;
Bram Moolenaar071d4272004-06-13 20:20:40 +000096typedef int ACCESS_MASK;
97typedef int BOOL;
98typedef int COLORREF;
99typedef int CONSOLE_CURSOR_INFO;
100typedef int COORD;
101typedef int DWORD;
102typedef int HANDLE;
Bram Moolenaaref269542016-01-19 13:22:12 +0100103typedef int LPHANDLE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000104typedef int HDC;
105typedef int HFONT;
106typedef int HICON;
107typedef int HINSTANCE;
108typedef int HWND;
109typedef int INPUT_RECORD;
110typedef int KEY_EVENT_RECORD;
111typedef int LOGFONT;
112typedef int LPBOOL;
113typedef int LPCTSTR;
114typedef int LPDWORD;
115typedef int LPSTR;
116typedef int LPTSTR;
117typedef int LPVOID;
118typedef int MOUSE_EVENT_RECORD;
119typedef int PACL;
120typedef int PDWORD;
121typedef int PHANDLE;
122typedef int PRINTDLG;
123typedef int PSECURITY_DESCRIPTOR;
124typedef int PSID;
125typedef int SECURITY_INFORMATION;
126typedef int SHORT;
127typedef int SMALL_RECT;
128typedef int TEXTMETRIC;
129typedef int TOKEN_INFORMATION_CLASS;
130typedef int TRUSTEE;
131typedef int WORD;
132typedef int WCHAR;
133typedef void VOID;
Bram Moolenaar82881492012-11-20 16:53:39 +0100134typedef int BY_HANDLE_FILE_INFORMATION;
Bram Moolenaar32ac8cd2013-07-03 18:49:17 +0200135typedef int SE_OBJECT_TYPE;
136typedef int PSNSECINFO;
137typedef int PSNSECINFOW;
Bram Moolenaarb8e0bdb2014-11-12 16:10:48 +0100138typedef int STARTUPINFO;
139typedef int PROCESS_INFORMATION;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000140#endif
141
142#ifndef FEAT_GUI_W32
143/* Undocumented API in kernel32.dll needed to work around dead key bug in
144 * console-mode applications in NT 4.0. If you switch keyboard layouts
145 * in a console app to a layout that includes dead keys and then hit a
146 * dead key, a call to ToAscii will trash the stack. My thanks to Ian James
147 * and Michael Dietrich for helping me figure out this workaround.
148 */
149
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100150/* WINAPI BOOL WINAPI GetConsoleKeyboardLayoutNameA(LPSTR); */
151#ifndef WINAPI
152# define WINAPI __stdcall
Bram Moolenaar071d4272004-06-13 20:20:40 +0000153#endif
154#if defined(__BORLANDC__)
155typedef BOOL (__stdcall *PFNGCKLN)(LPSTR);
156#else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100157typedef BOOL (WINAPI *PFNGCKLN)(LPSTR);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000158#endif
Bram Moolenaard6f676d2005-06-01 21:51:55 +0000159static PFNGCKLN s_pfnGetConsoleKeyboardLayoutName = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160#endif
161
162#if defined(__BORLANDC__)
163/* Strangely Borland uses a non-standard name. */
164# define wcsicmp(a, b) wcscmpi((a), (b))
165#endif
166
Bram Moolenaar82881492012-11-20 16:53:39 +0100167#ifndef PROTO
168
Bram Moolenaar84a05ac2013-05-06 04:24:17 +0200169/* Enable common dialogs input unicode from IME if possible. */
Bram Moolenaar8c85fa32011-08-10 17:08:03 +0200170#ifdef FEAT_MBYTE
Bram Moolenaaraf62ff32013-03-19 14:48:29 +0100171LRESULT (WINAPI *pDispatchMessage)(CONST MSG *) = DispatchMessage;
Bram Moolenaar8c85fa32011-08-10 17:08:03 +0200172BOOL (WINAPI *pGetMessage)(LPMSG, HWND, UINT, UINT) = GetMessage;
173BOOL (WINAPI *pIsDialogMessage)(HWND, LPMSG) = IsDialogMessage;
174BOOL (WINAPI *pPeekMessage)(LPMSG, HWND, UINT, UINT, UINT) = PeekMessage;
175#endif
176
Bram Moolenaar82881492012-11-20 16:53:39 +0100177#endif /* PROTO */
178
Bram Moolenaar071d4272004-06-13 20:20:40 +0000179#ifndef FEAT_GUI_W32
180/* Win32 Console handles for input and output */
181static HANDLE g_hConIn = INVALID_HANDLE_VALUE;
182static HANDLE g_hConOut = INVALID_HANDLE_VALUE;
183
184/* Win32 Screen buffer,coordinate,console I/O information */
185static SMALL_RECT g_srScrollRegion;
186static COORD g_coord; /* 0-based, but external coords are 1-based */
187
188/* The attribute of the screen when the editor was started */
189static WORD g_attrDefault = 7; /* lightgray text on black background */
190static WORD g_attrCurrent;
191
192static int g_fCBrkPressed = FALSE; /* set by ctrl-break interrupt */
193static int g_fCtrlCPressed = FALSE; /* set when ctrl-C or ctrl-break detected */
194static int g_fForceExit = FALSE; /* set when forcefully exiting */
195
196static void termcap_mode_start(void);
197static void termcap_mode_end(void);
198static void clear_chars(COORD coord, DWORD n);
199static void clear_screen(void);
200static void clear_to_end_of_display(void);
201static void clear_to_end_of_line(void);
202static void scroll(unsigned cLines);
203static void set_scroll_region(unsigned left, unsigned top,
204 unsigned right, unsigned bottom);
205static void insert_lines(unsigned cLines);
206static void delete_lines(unsigned cLines);
207static void gotoxy(unsigned x, unsigned y);
208static void normvideo(void);
209static void textattr(WORD wAttr);
210static void textcolor(WORD wAttr);
211static void textbackground(WORD wAttr);
212static void standout(void);
213static void standend(void);
214static void visual_bell(void);
215static void cursor_visible(BOOL fVisible);
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200216static DWORD write_chars(char_u *pchBuf, DWORD cbToWrite);
217static WCHAR tgetch(int *pmodifiers, WCHAR *pch2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000218static void create_conin(void);
219static int s_cursor_visible = TRUE;
220static int did_create_conin = FALSE;
221#else
222static int s_dont_use_vimrun = TRUE;
223static int need_vimrun_warning = FALSE;
224static char *vimrun_path = "vimrun ";
225#endif
226
Bram Moolenaar12b559e2013-06-12 22:41:37 +0200227static int win32_getattrs(char_u *name);
228static int win32_setattrs(char_u *name, int attrs);
229static int win32_set_archive(char_u *name);
230
Bram Moolenaar071d4272004-06-13 20:20:40 +0000231#ifndef FEAT_GUI_W32
232static int suppress_winsize = 1; /* don't fiddle with console */
233#endif
234
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200235static char_u *exe_path = NULL;
236
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100237static BOOL win8_or_later = FALSE;
238
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100239#ifndef FEAT_GUI_W32
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100240/*
241 * Version of ReadConsoleInput() that works with IME.
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100242 * Works around problems on Windows 8.
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100243 */
244 static BOOL
245read_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100246 HANDLE hInput,
247 INPUT_RECORD *lpBuffer,
248 DWORD nLength,
249 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100250{
251 enum
252 {
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100253 IRSIZE = 10
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100254 };
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100255 static INPUT_RECORD s_irCache[IRSIZE];
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100256 static DWORD s_dwIndex = 0;
257 static DWORD s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100258 DWORD dwEvents;
Bram Moolenaardd415a62014-02-05 14:02:27 +0100259 int head;
260 int tail;
261 int i;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100262
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200263 if (nLength == -2)
264 return (s_dwMax > 0) ? TRUE : FALSE;
265
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100266 if (!win8_or_later)
267 {
268 if (nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200269 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
270 return ReadConsoleInputW(hInput, lpBuffer, 1, &dwEvents);
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100271 }
272
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100273 if (s_dwMax == 0)
274 {
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100275 if (nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200276 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
277 if (!ReadConsoleInputW(hInput, s_irCache, IRSIZE, &dwEvents))
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100278 return FALSE;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100279 s_dwIndex = 0;
280 s_dwMax = dwEvents;
281 if (dwEvents == 0)
282 {
283 *lpEvents = 0;
284 return TRUE;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100285 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100286
287 if (s_dwMax > 1)
288 {
289 head = 0;
290 tail = s_dwMax - 1;
291 while (head != tail)
292 {
293 if (s_irCache[head].EventType == WINDOW_BUFFER_SIZE_EVENT
294 && s_irCache[head + 1].EventType
295 == WINDOW_BUFFER_SIZE_EVENT)
296 {
297 /* Remove duplicate event to avoid flicker. */
298 for (i = head; i < tail; ++i)
299 s_irCache[i] = s_irCache[i + 1];
300 --tail;
301 continue;
302 }
303 head++;
304 }
305 s_dwMax = tail + 1;
306 }
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100307 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100308
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100309 *lpBuffer = s_irCache[s_dwIndex];
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200310 if (!(nLength == -1 || nLength == -2) && ++s_dwIndex >= s_dwMax)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100311 s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100312 *lpEvents = 1;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100313 return TRUE;
314}
315
316/*
317 * Version of PeekConsoleInput() that works with IME.
318 */
319 static BOOL
320peek_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100321 HANDLE hInput,
322 INPUT_RECORD *lpBuffer,
323 DWORD nLength,
324 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100325{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100326 return read_console_input(hInput, lpBuffer, -1, lpEvents);
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100327}
328
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100329# ifdef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200330 static DWORD
331msg_wait_for_multiple_objects(
332 DWORD nCount,
333 LPHANDLE pHandles,
334 BOOL fWaitAll,
335 DWORD dwMilliseconds,
336 DWORD dwWakeMask)
337{
338 if (read_console_input(NULL, NULL, -2, NULL))
339 return WAIT_OBJECT_0;
340 return MsgWaitForMultipleObjects(nCount, pHandles, fWaitAll,
341 dwMilliseconds, dwWakeMask);
342}
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100343# endif
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200344
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100345# ifndef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200346 static DWORD
347wait_for_single_object(
348 HANDLE hHandle,
349 DWORD dwMilliseconds)
350{
351 if (read_console_input(NULL, NULL, -2, NULL))
352 return WAIT_OBJECT_0;
353 return WaitForSingleObject(hHandle, dwMilliseconds);
354}
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100355# endif
356#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100357#endif
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200358
Bram Moolenaar071d4272004-06-13 20:20:40 +0000359 static void
360get_exe_name(void)
361{
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100362 /* Maximum length of $PATH is more than MAXPATHL. 8191 is often mentioned
363 * as the maximum length that works (plus a NUL byte). */
364#define MAX_ENV_PATH_LEN 8192
365 char temp[MAX_ENV_PATH_LEN];
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200366 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000367
368 if (exe_name == NULL)
369 {
370 /* store the name of the executable, may be used for $VIM */
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100371 GetModuleFileName(NULL, temp, MAX_ENV_PATH_LEN - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000372 if (*temp != NUL)
373 exe_name = FullName_save((char_u *)temp, FALSE);
374 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000375
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200376 if (exe_path == NULL && exe_name != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000377 {
Bram Moolenaar6b5ef062010-10-27 12:18:00 +0200378 exe_path = vim_strnsave(exe_name,
379 (int)(gettail_sep(exe_name) - exe_name));
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200380 if (exe_path != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000381 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200382 /* Append our starting directory to $PATH, so that when doing
383 * "!xxd" it's found in our starting directory. Needed because
384 * SearchPath() also looks there. */
385 p = mch_getenv("PATH");
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100386 if (p == NULL
387 || STRLEN(p) + STRLEN(exe_path) + 2 < MAX_ENV_PATH_LEN)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200388 {
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100389 if (p == NULL || *p == NUL)
390 temp[0] = NUL;
391 else
392 {
393 STRCPY(temp, p);
394 STRCAT(temp, ";");
395 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200396 STRCAT(temp, exe_path);
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100397 vim_setenv((char_u *)"PATH", (char_u *)temp);
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200398 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000399 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000400 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000401}
402
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200403/*
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100404 * Unescape characters in "p" that appear in "escaped".
405 */
406 static void
407unescape_shellxquote(char_u *p, char_u *escaped)
408{
Bram Moolenaar4336cdf2012-02-29 13:58:47 +0100409 int l = (int)STRLEN(p);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100410 int n;
411
412 while (*p != NUL)
413 {
414 if (*p == '^' && vim_strchr(escaped, p[1]) != NULL)
415 mch_memmove(p, p + 1, l--);
416#ifdef FEAT_MBYTE
417 n = (*mb_ptr2len)(p);
418#else
419 n = 1;
420#endif
421 p += n;
422 l -= n;
423 }
424}
425
426/*
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200427 * Load library "name".
428 */
429 HINSTANCE
430vimLoadLib(char *name)
431{
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200432 HINSTANCE dll = NULL;
433 char old_dir[MAXPATHL];
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200434
Bram Moolenaarfaca8402012-10-21 02:37:10 +0200435 /* NOTE: Do not use mch_dirname() and mch_chdir() here, they may call
436 * vimLoadLib() recursively, which causes a stack overflow. */
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200437 if (exe_path == NULL)
438 get_exe_name();
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200439 if (exe_path != NULL)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200440 {
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200441#ifdef FEAT_MBYTE
442 WCHAR old_dirw[MAXPATHL];
443
444 if (GetCurrentDirectoryW(MAXPATHL, old_dirw) != 0)
445 {
446 /* Change directory to where the executable is, both to make
447 * sure we find a .dll there and to avoid looking for a .dll
448 * in the current directory. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100449 SetCurrentDirectory((LPCSTR)exe_path);
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200450 dll = LoadLibrary(name);
451 SetCurrentDirectoryW(old_dirw);
452 return dll;
453 }
454 /* Retry with non-wide function (for Windows 98). */
455 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
456#endif
457 if (GetCurrentDirectory(MAXPATHL, old_dir) != 0)
458 {
459 /* Change directory to where the executable is, both to make
460 * sure we find a .dll there and to avoid looking for a .dll
461 * in the current directory. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100462 SetCurrentDirectory((LPCSTR)exe_path);
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200463 dll = LoadLibrary(name);
464 SetCurrentDirectory(old_dir);
465 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200466 }
467 return dll;
468}
469
Bram Moolenaar071d4272004-06-13 20:20:40 +0000470#if defined(DYNAMIC_GETTEXT) || defined(PROTO)
471# ifndef GETTEXT_DLL
472# define GETTEXT_DLL "libintl.dll"
Bram Moolenaar286eacd2016-01-16 18:05:50 +0100473# define GETTEXT_DLL_ALT "libintl-8.dll"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000474# endif
Bram Moolenaarf6a2b082012-06-29 13:14:03 +0200475/* Dummy functions */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000476static char *null_libintl_gettext(const char *);
477static char *null_libintl_textdomain(const char *);
478static char *null_libintl_bindtextdomain(const char *, const char *);
479static char *null_libintl_bind_textdomain_codeset(const char *, const char *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000480
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200481static HINSTANCE hLibintlDLL = NULL;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000482char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
483char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
484char *(*dyn_libintl_bindtextdomain)(const char *, const char *)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000485 = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000486char *(*dyn_libintl_bind_textdomain_codeset)(const char *, const char *)
487 = null_libintl_bind_textdomain_codeset;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000488
489 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100490dyn_libintl_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000491{
492 int i;
493 static struct
494 {
495 char *name;
496 FARPROC *ptr;
497 } libintl_entry[] =
498 {
499 {"gettext", (FARPROC*)&dyn_libintl_gettext},
500 {"textdomain", (FARPROC*)&dyn_libintl_textdomain},
501 {"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
502 {NULL, NULL}
503 };
504
505 /* No need to initialize twice. */
506 if (hLibintlDLL)
507 return 1;
508 /* Load gettext library (libintl.dll) */
Bram Moolenaar923e43b2016-01-28 15:07:38 +0100509 hLibintlDLL = vimLoadLib(GETTEXT_DLL);
Bram Moolenaar938ee832016-01-24 15:36:03 +0100510#ifdef GETTEXT_DLL_ALT
Bram Moolenaar286eacd2016-01-16 18:05:50 +0100511 if (!hLibintlDLL)
512 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT);
Bram Moolenaar938ee832016-01-24 15:36:03 +0100513#endif
514 if (!hLibintlDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200516 if (p_verbose > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000517 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200518 verbose_enter();
519 EMSG2(_(e_loadlib), GETTEXT_DLL);
520 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000521 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200522 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523 }
524 for (i = 0; libintl_entry[i].name != NULL
525 && libintl_entry[i].ptr != NULL; ++i)
526 {
527 if ((*libintl_entry[i].ptr = (FARPROC)GetProcAddress(hLibintlDLL,
528 libintl_entry[i].name)) == NULL)
529 {
530 dyn_libintl_end();
531 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000532 {
533 verbose_enter();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000534 EMSG2(_(e_loadfunc), libintl_entry[i].name);
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000535 verbose_leave();
536 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000537 return 0;
538 }
539 }
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000540
541 /* The bind_textdomain_codeset() function is optional. */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000542 dyn_libintl_bind_textdomain_codeset = (void *)GetProcAddress(hLibintlDLL,
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000543 "bind_textdomain_codeset");
544 if (dyn_libintl_bind_textdomain_codeset == NULL)
545 dyn_libintl_bind_textdomain_codeset =
546 null_libintl_bind_textdomain_codeset;
547
Bram Moolenaar071d4272004-06-13 20:20:40 +0000548 return 1;
549}
550
551 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100552dyn_libintl_end(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000553{
554 if (hLibintlDLL)
555 FreeLibrary(hLibintlDLL);
556 hLibintlDLL = NULL;
557 dyn_libintl_gettext = null_libintl_gettext;
558 dyn_libintl_textdomain = null_libintl_textdomain;
559 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000560 dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000561}
562
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000563/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000564 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000565null_libintl_gettext(const char *msgid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566{
567 return (char*)msgid;
568}
569
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000570/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000571 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000572null_libintl_bindtextdomain(const char *domainname, const char *dirname)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000573{
574 return NULL;
575}
576
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000577/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000578 static char *
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000579null_libintl_bind_textdomain_codeset(const char *domainname,
580 const char *codeset)
581{
582 return NULL;
583}
584
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000585/*ARGSUSED*/
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000586 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000587null_libintl_textdomain(const char *domainname)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000588{
589 return NULL;
590}
591
592#endif /* DYNAMIC_GETTEXT */
593
594/* This symbol is not defined in older versions of the SDK or Visual C++ */
595
596#ifndef VER_PLATFORM_WIN32_WINDOWS
597# define VER_PLATFORM_WIN32_WINDOWS 1
598#endif
599
600DWORD g_PlatformId;
601
602#ifdef HAVE_ACL
Bram Moolenaar82881492012-11-20 16:53:39 +0100603# ifndef PROTO
604# include <aclapi.h>
605# endif
Bram Moolenaar27515922013-06-29 15:36:26 +0200606# ifndef PROTECTED_DACL_SECURITY_INFORMATION
607# define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000L
608# endif
Bram Moolenaar82881492012-11-20 16:53:39 +0100609
Bram Moolenaar071d4272004-06-13 20:20:40 +0000610/*
611 * These are needed to dynamically load the ADVAPI DLL, which is not
612 * implemented under Windows 95 (and causes VIM to crash)
613 */
Bram Moolenaar39efa892013-06-29 15:40:04 +0200614typedef DWORD (WINAPI *PSNSECINFO) (LPSTR, SE_OBJECT_TYPE,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000615 SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
Bram Moolenaar39efa892013-06-29 15:40:04 +0200616typedef DWORD (WINAPI *PGNSECINFO) (LPSTR, SE_OBJECT_TYPE,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000617 SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *,
618 PSECURITY_DESCRIPTOR *);
Bram Moolenaar27515922013-06-29 15:36:26 +0200619# ifdef FEAT_MBYTE
Bram Moolenaar39efa892013-06-29 15:40:04 +0200620typedef DWORD (WINAPI *PSNSECINFOW) (LPWSTR, SE_OBJECT_TYPE,
Bram Moolenaar27515922013-06-29 15:36:26 +0200621 SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
Bram Moolenaar39efa892013-06-29 15:40:04 +0200622typedef DWORD (WINAPI *PGNSECINFOW) (LPWSTR, SE_OBJECT_TYPE,
Bram Moolenaar27515922013-06-29 15:36:26 +0200623 SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *,
624 PSECURITY_DESCRIPTOR *);
625# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000626
627static HANDLE advapi_lib = NULL; /* Handle for ADVAPI library */
628static PSNSECINFO pSetNamedSecurityInfo;
629static PGNSECINFO pGetNamedSecurityInfo;
Bram Moolenaar27515922013-06-29 15:36:26 +0200630# ifdef FEAT_MBYTE
631static PSNSECINFOW pSetNamedSecurityInfoW;
632static PGNSECINFOW pGetNamedSecurityInfoW;
633# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000634#endif
635
Bram Moolenaar4b9669f2011-07-07 16:20:52 +0200636typedef BOOL (WINAPI *PSETHANDLEINFORMATION)(HANDLE, DWORD, DWORD);
637
638static BOOL allowPiping = FALSE;
639static PSETHANDLEINFORMATION pSetHandleInformation;
640
Bram Moolenaar27515922013-06-29 15:36:26 +0200641#ifdef HAVE_ACL
642/*
643 * Enables or disables the specified privilege.
644 */
645 static BOOL
646win32_enable_privilege(LPTSTR lpszPrivilege, BOOL bEnable)
647{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100648 BOOL bResult;
649 LUID luid;
650 HANDLE hToken;
651 TOKEN_PRIVILEGES tokenPrivileges;
Bram Moolenaar27515922013-06-29 15:36:26 +0200652
653 if (!OpenProcessToken(GetCurrentProcess(),
654 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
655 return FALSE;
656
657 if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
658 {
659 CloseHandle(hToken);
660 return FALSE;
661 }
662
Bram Moolenaar45500912014-07-09 20:51:07 +0200663 tokenPrivileges.PrivilegeCount = 1;
Bram Moolenaar27515922013-06-29 15:36:26 +0200664 tokenPrivileges.Privileges[0].Luid = luid;
665 tokenPrivileges.Privileges[0].Attributes = bEnable ?
666 SE_PRIVILEGE_ENABLED : 0;
667
668 bResult = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges,
669 sizeof(TOKEN_PRIVILEGES), NULL, NULL);
670
671 CloseHandle(hToken);
672
673 return bResult && GetLastError() == ERROR_SUCCESS;
674}
675#endif
676
Bram Moolenaar071d4272004-06-13 20:20:40 +0000677/*
678 * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
679 * VER_PLATFORM_WIN32_WINDOWS (Win95).
680 */
681 void
682PlatformId(void)
683{
684 static int done = FALSE;
685
686 if (!done)
687 {
688 OSVERSIONINFO ovi;
689
690 ovi.dwOSVersionInfoSize = sizeof(ovi);
691 GetVersionEx(&ovi);
692
693 g_PlatformId = ovi.dwPlatformId;
694
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100695 if ((ovi.dwMajorVersion == 6 && ovi.dwMinorVersion >= 2)
696 || ovi.dwMajorVersion > 6)
697 win8_or_later = TRUE;
698
Bram Moolenaar071d4272004-06-13 20:20:40 +0000699#ifdef HAVE_ACL
700 /*
701 * Load the ADVAPI runtime if we are on anything
702 * other than Windows 95
703 */
704 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
705 {
706 /*
707 * do this load. Problems: Doesn't unload at end of run (this is
708 * theoretically okay, since Windows should unload it when VIM
709 * terminates). Should we be using the 'mch_libcall' routines?
710 * Seems like a lot of overhead to load/unload ADVAPI32.DLL each
711 * time we verify security...
712 */
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200713 advapi_lib = vimLoadLib("ADVAPI32.DLL");
Bram Moolenaar071d4272004-06-13 20:20:40 +0000714 if (advapi_lib != NULL)
715 {
716 pSetNamedSecurityInfo = (PSNSECINFO)GetProcAddress(advapi_lib,
717 "SetNamedSecurityInfoA");
718 pGetNamedSecurityInfo = (PGNSECINFO)GetProcAddress(advapi_lib,
719 "GetNamedSecurityInfoA");
Bram Moolenaar27515922013-06-29 15:36:26 +0200720# ifdef FEAT_MBYTE
721 pSetNamedSecurityInfoW = (PSNSECINFOW)GetProcAddress(advapi_lib,
722 "SetNamedSecurityInfoW");
723 pGetNamedSecurityInfoW = (PGNSECINFOW)GetProcAddress(advapi_lib,
724 "GetNamedSecurityInfoW");
725# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000726 if (pSetNamedSecurityInfo == NULL
Bram Moolenaar27515922013-06-29 15:36:26 +0200727 || pGetNamedSecurityInfo == NULL
728# ifdef FEAT_MBYTE
729 || pSetNamedSecurityInfoW == NULL
730 || pGetNamedSecurityInfoW == NULL
731# endif
732 )
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733 {
734 /* If we can't get the function addresses, set advapi_lib
735 * to NULL so that we don't use them. */
736 FreeLibrary(advapi_lib);
737 advapi_lib = NULL;
738 }
Bram Moolenaar27515922013-06-29 15:36:26 +0200739 /* Enable privilege for getting or setting SACLs. */
740 win32_enable_privilege(SE_SECURITY_NAME, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000741 }
742 }
743#endif
Bram Moolenaar4b9669f2011-07-07 16:20:52 +0200744 /*
745 * If we are on windows NT, try to load the pipe functions, only
746 * available from Win2K.
747 */
748 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
749 {
750 HANDLE kernel32 = GetModuleHandle("kernel32");
751 pSetHandleInformation = (PSETHANDLEINFORMATION)GetProcAddress(
752 kernel32, "SetHandleInformation");
753
754 allowPiping = pSetHandleInformation != NULL;
755 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000756 done = TRUE;
757 }
758}
759
760/*
761 * Return TRUE when running on Windows 95 (or 98 or ME).
762 * Only to be used after mch_init().
763 */
764 int
765mch_windows95(void)
766{
767 return g_PlatformId == VER_PLATFORM_WIN32_WINDOWS;
768}
769
770#ifdef FEAT_GUI_W32
771/*
772 * Used to work around the "can't do synchronous spawn"
773 * problem on Win32s, without resorting to Universal Thunk.
774 */
775static int old_num_windows;
776static int num_windows;
777
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000778/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779 static BOOL CALLBACK
780win32ssynch_cb(HWND hwnd, LPARAM lparam)
781{
782 num_windows++;
783 return TRUE;
784}
785#endif
786
787#ifndef FEAT_GUI_W32
788
789#define SHIFT (SHIFT_PRESSED)
790#define CTRL (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
791#define ALT (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
792#define ALT_GR (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)
793
794
795/* When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
796 * We map function keys to their ANSI terminal equivalents, as produced
797 * by ANSI.SYS, for compatibility with the MS-DOS version of Vim. Any
798 * ANSI key with a value >= '\300' is nonstandard, but provided anyway
799 * so that the user can have access to all SHIFT-, CTRL-, and ALT-
800 * combinations of function/arrow/etc keys.
801 */
802
Bram Moolenaard6f676d2005-06-01 21:51:55 +0000803static const struct
Bram Moolenaar071d4272004-06-13 20:20:40 +0000804{
805 WORD wVirtKey;
806 BOOL fAnsiKey;
807 int chAlone;
808 int chShift;
809 int chCtrl;
810 int chAlt;
811} VirtKeyMap[] =
812{
813
814/* Key ANSI alone shift ctrl alt */
815 { VK_ESCAPE,FALSE, ESC, ESC, ESC, ESC, },
816
817 { VK_F1, TRUE, ';', 'T', '^', 'h', },
818 { VK_F2, TRUE, '<', 'U', '_', 'i', },
819 { VK_F3, TRUE, '=', 'V', '`', 'j', },
820 { VK_F4, TRUE, '>', 'W', 'a', 'k', },
821 { VK_F5, TRUE, '?', 'X', 'b', 'l', },
822 { VK_F6, TRUE, '@', 'Y', 'c', 'm', },
823 { VK_F7, TRUE, 'A', 'Z', 'd', 'n', },
824 { VK_F8, TRUE, 'B', '[', 'e', 'o', },
825 { VK_F9, TRUE, 'C', '\\', 'f', 'p', },
826 { VK_F10, TRUE, 'D', ']', 'g', 'q', },
827 { VK_F11, TRUE, '\205', '\207', '\211', '\213', },
828 { VK_F12, TRUE, '\206', '\210', '\212', '\214', },
829
830 { VK_HOME, TRUE, 'G', '\302', 'w', '\303', },
831 { VK_UP, TRUE, 'H', '\304', '\305', '\306', },
832 { VK_PRIOR, TRUE, 'I', '\307', '\204', '\310', }, /*PgUp*/
833 { VK_LEFT, TRUE, 'K', '\311', 's', '\312', },
834 { VK_RIGHT, TRUE, 'M', '\313', 't', '\314', },
835 { VK_END, TRUE, 'O', '\315', 'u', '\316', },
836 { VK_DOWN, TRUE, 'P', '\317', '\320', '\321', },
837 { VK_NEXT, TRUE, 'Q', '\322', 'v', '\323', }, /*PgDn*/
838 { VK_INSERT,TRUE, 'R', '\324', '\325', '\326', },
839 { VK_DELETE,TRUE, 'S', '\327', '\330', '\331', },
840
841 { VK_SNAPSHOT,TRUE, 0, 0, 0, 'r', }, /*PrtScrn*/
842
843#if 0
844 /* Most people don't have F13-F20, but what the hell... */
845 { VK_F13, TRUE, '\332', '\333', '\334', '\335', },
846 { VK_F14, TRUE, '\336', '\337', '\340', '\341', },
847 { VK_F15, TRUE, '\342', '\343', '\344', '\345', },
848 { VK_F16, TRUE, '\346', '\347', '\350', '\351', },
849 { VK_F17, TRUE, '\352', '\353', '\354', '\355', },
850 { VK_F18, TRUE, '\356', '\357', '\360', '\361', },
851 { VK_F19, TRUE, '\362', '\363', '\364', '\365', },
852 { VK_F20, TRUE, '\366', '\367', '\370', '\371', },
853#endif
854 { VK_ADD, TRUE, 'N', 'N', 'N', 'N', }, /* keyp '+' */
855 { VK_SUBTRACT, TRUE,'J', 'J', 'J', 'J', }, /* keyp '-' */
856 /* { VK_DIVIDE, TRUE,'N', 'N', 'N', 'N', }, keyp '/' */
857 { VK_MULTIPLY, TRUE,'7', '7', '7', '7', }, /* keyp '*' */
858
859 { VK_NUMPAD0,TRUE, '\332', '\333', '\334', '\335', },
860 { VK_NUMPAD1,TRUE, '\336', '\337', '\340', '\341', },
861 { VK_NUMPAD2,TRUE, '\342', '\343', '\344', '\345', },
862 { VK_NUMPAD3,TRUE, '\346', '\347', '\350', '\351', },
863 { VK_NUMPAD4,TRUE, '\352', '\353', '\354', '\355', },
864 { VK_NUMPAD5,TRUE, '\356', '\357', '\360', '\361', },
865 { VK_NUMPAD6,TRUE, '\362', '\363', '\364', '\365', },
866 { VK_NUMPAD7,TRUE, '\366', '\367', '\370', '\371', },
867 { VK_NUMPAD8,TRUE, '\372', '\373', '\374', '\375', },
868 /* Sorry, out of number space! <negri>*/
869 { VK_NUMPAD9,TRUE, '\376', '\377', '\377', '\367', },
870
871};
872
873
874#ifdef _MSC_VER
875// The ToAscii bug destroys several registers. Need to turn off optimization
876// or the GetConsoleKeyboardLayoutName hack will fail in non-debug versions
Bram Moolenaar7b5f8322006-03-23 22:47:08 +0000877# pragma warning(push)
878# pragma warning(disable: 4748)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000879# pragma optimize("", off)
880#endif
881
882#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200883# define UChar UnicodeChar
Bram Moolenaar071d4272004-06-13 20:20:40 +0000884#else
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200885# define UChar uChar.UnicodeChar
Bram Moolenaar071d4272004-06-13 20:20:40 +0000886#endif
887
888/* The return code indicates key code size. */
889 static int
890#ifdef __BORLANDC__
891 __stdcall
892#endif
893win32_kbd_patch_key(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000894 KEY_EVENT_RECORD *pker)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000895{
896 UINT uMods = pker->dwControlKeyState;
897 static int s_iIsDead = 0;
898 static WORD awAnsiCode[2];
899 static BYTE abKeystate[256];
900
901
902 if (s_iIsDead == 2)
903 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200904 pker->UChar = (WCHAR) awAnsiCode[1];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000905 s_iIsDead = 0;
906 return 1;
907 }
908
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200909 if (pker->UChar != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000910 return 1;
911
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200912 vim_memset(abKeystate, 0, sizeof (abKeystate));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000913
914 // Should only be non-NULL on NT 4.0
915 if (s_pfnGetConsoleKeyboardLayoutName != NULL)
916 {
917 CHAR szKLID[KL_NAMELENGTH];
918
919 if ((*s_pfnGetConsoleKeyboardLayoutName)(szKLID))
920 (void)LoadKeyboardLayout(szKLID, KLF_ACTIVATE);
921 }
922
923 /* Clear any pending dead keys */
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200924 ToUnicode(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000925
926 if (uMods & SHIFT_PRESSED)
927 abKeystate[VK_SHIFT] = 0x80;
928 if (uMods & CAPSLOCK_ON)
929 abKeystate[VK_CAPITAL] = 1;
930
931 if ((uMods & ALT_GR) == ALT_GR)
932 {
933 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
934 abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
935 }
936
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200937 s_iIsDead = ToUnicode(pker->wVirtualKeyCode, pker->wVirtualScanCode,
938 abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000939
940 if (s_iIsDead > 0)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200941 pker->UChar = (WCHAR) awAnsiCode[0];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000942
943 return s_iIsDead;
944}
945
946#ifdef _MSC_VER
947/* MUST switch optimization on again here, otherwise a call to
948 * decode_key_event() may crash (e.g. when hitting caps-lock) */
949# pragma optimize("", on)
Bram Moolenaar7b5f8322006-03-23 22:47:08 +0000950# pragma warning(pop)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000951
952# if (_MSC_VER < 1100)
953/* MUST turn off global optimisation for this next function, or
954 * pressing ctrl-minus in insert mode crashes Vim when built with
955 * VC4.1. -- negri. */
956# pragma optimize("g", off)
957# endif
958#endif
959
960static BOOL g_fJustGotFocus = FALSE;
961
962/*
963 * Decode a KEY_EVENT into one or two keystrokes
964 */
965 static BOOL
966decode_key_event(
967 KEY_EVENT_RECORD *pker,
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200968 WCHAR *pch,
969 WCHAR *pch2,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000970 int *pmodifiers,
971 BOOL fDoPost)
972{
973 int i;
974 const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
975
976 *pch = *pch2 = NUL;
977 g_fJustGotFocus = FALSE;
978
979 /* ignore key up events */
980 if (!pker->bKeyDown)
981 return FALSE;
982
983 /* ignore some keystrokes */
984 switch (pker->wVirtualKeyCode)
985 {
986 /* modifiers */
987 case VK_SHIFT:
988 case VK_CONTROL:
989 case VK_MENU: /* Alt key */
990 return FALSE;
991
992 default:
993 break;
994 }
995
996 /* special cases */
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200997 if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->UChar == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000998 {
999 /* Ctrl-6 is Ctrl-^ */
1000 if (pker->wVirtualKeyCode == '6')
1001 {
1002 *pch = Ctrl_HAT;
1003 return TRUE;
1004 }
1005 /* Ctrl-2 is Ctrl-@ */
1006 else if (pker->wVirtualKeyCode == '2')
1007 {
1008 *pch = NUL;
1009 return TRUE;
1010 }
1011 /* Ctrl-- is Ctrl-_ */
1012 else if (pker->wVirtualKeyCode == 0xBD)
1013 {
1014 *pch = Ctrl__;
1015 return TRUE;
1016 }
1017 }
1018
1019 /* Shift-TAB */
1020 if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED))
1021 {
1022 *pch = K_NUL;
1023 *pch2 = '\017';
1024 return TRUE;
1025 }
1026
1027 for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]); --i >= 0; )
1028 {
1029 if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
1030 {
1031 if (nModifs == 0)
1032 *pch = VirtKeyMap[i].chAlone;
1033 else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
1034 *pch = VirtKeyMap[i].chShift;
1035 else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
1036 *pch = VirtKeyMap[i].chCtrl;
1037 else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
1038 *pch = VirtKeyMap[i].chAlt;
1039
1040 if (*pch != 0)
1041 {
1042 if (VirtKeyMap[i].fAnsiKey)
1043 {
1044 *pch2 = *pch;
1045 *pch = K_NUL;
1046 }
1047
1048 return TRUE;
1049 }
1050 }
1051 }
1052
1053 i = win32_kbd_patch_key(pker);
1054
1055 if (i < 0)
1056 *pch = NUL;
1057 else
1058 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001059 *pch = (i > 0) ? pker->UChar : NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001060
1061 if (pmodifiers != NULL)
1062 {
1063 /* Pass on the ALT key as a modifier, but only when not combined
1064 * with CTRL (which is ALTGR). */
1065 if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0)
1066 *pmodifiers |= MOD_MASK_ALT;
1067
1068 /* Pass on SHIFT only for special keys, because we don't know when
1069 * it's already included with the character. */
1070 if ((nModifs & SHIFT) != 0 && *pch <= 0x20)
1071 *pmodifiers |= MOD_MASK_SHIFT;
1072
1073 /* Pass on CTRL only for non-special keys, because we don't know
1074 * when it's already included with the character. And not when
1075 * combined with ALT (which is ALTGR). */
1076 if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0
1077 && *pch >= 0x20 && *pch < 0x80)
1078 *pmodifiers |= MOD_MASK_CTRL;
1079 }
1080 }
1081
1082 return (*pch != NUL);
1083}
1084
1085#ifdef _MSC_VER
1086# pragma optimize("", on)
1087#endif
1088
1089#endif /* FEAT_GUI_W32 */
1090
1091
1092#ifdef FEAT_MOUSE
1093
1094/*
1095 * For the GUI the mouse handling is in gui_w32.c.
1096 */
1097# ifdef FEAT_GUI_W32
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00001098/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00001099 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001100mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001101{
1102}
1103# else
1104static int g_fMouseAvail = FALSE; /* mouse present */
1105static int g_fMouseActive = FALSE; /* mouse enabled */
1106static int g_nMouseClick = -1; /* mouse status */
1107static int g_xMouse; /* mouse x coordinate */
1108static int g_yMouse; /* mouse y coordinate */
1109
1110/*
1111 * Enable or disable mouse input
1112 */
1113 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001114mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001115{
1116 DWORD cmodein;
1117
1118 if (!g_fMouseAvail)
1119 return;
1120
1121 g_fMouseActive = on;
1122 GetConsoleMode(g_hConIn, &cmodein);
1123
1124 if (g_fMouseActive)
1125 cmodein |= ENABLE_MOUSE_INPUT;
1126 else
1127 cmodein &= ~ENABLE_MOUSE_INPUT;
1128
1129 SetConsoleMode(g_hConIn, cmodein);
1130}
1131
Bram Moolenaar4d919d72016-02-05 22:36:41 +01001132#ifdef FEAT_CHANNEL
1133 static int
1134handle_channel_event(void)
1135{
1136 int ret;
1137 fd_set rfds;
1138 int maxfd;
1139
1140 FD_ZERO(&rfds);
1141 maxfd = channel_select_setup(-1, &rfds);
1142 if (maxfd >= 0)
1143 {
1144 struct timeval tv;
1145
1146 tv.tv_sec = 0;
1147 tv.tv_usec = 0;
1148 ret = select(maxfd + 1, &rfds, NULL, NULL, &tv);
1149 if (ret > 0 && channel_select_check(ret, &rfds) > 0)
1150 return TRUE;
1151 }
1152 return FALSE;
1153}
1154#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001155
1156/*
1157 * Decode a MOUSE_EVENT. If it's a valid event, return MOUSE_LEFT,
1158 * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
1159 * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
1160 * or a MOUSE_LEFT, _MIDDLE, or _RIGHT. We encode the button type,
1161 * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
1162 * and we return the mouse position in g_xMouse and g_yMouse.
1163 *
1164 * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
1165 * MOUSE_DRAGs and one MOUSE_RELEASE. MOUSE_RELEASE will be followed only
1166 * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
1167 *
1168 * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
1169 * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
1170 *
1171 * Windows will send us MOUSE_MOVED notifications whenever the mouse
1172 * moves, even if it stays within the same character cell. We ignore
1173 * all MOUSE_MOVED messages if the position hasn't really changed, and
1174 * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
1175 * we're only interested in MOUSE_DRAG).
1176 *
1177 * All of this is complicated by the code that fakes MOUSE_MIDDLE on
1178 * 2-button mouses by pressing the left & right buttons simultaneously.
1179 * In practice, it's almost impossible to click both at the same time,
1180 * so we need to delay a little. Also, we tend not to get MOUSE_RELEASE
1181 * in such cases, if the user is clicking quickly.
1182 */
1183 static BOOL
1184decode_mouse_event(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001185 MOUSE_EVENT_RECORD *pmer)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001186{
1187 static int s_nOldButton = -1;
1188 static int s_nOldMouseClick = -1;
1189 static int s_xOldMouse = -1;
1190 static int s_yOldMouse = -1;
1191 static linenr_T s_old_topline = 0;
1192#ifdef FEAT_DIFF
1193 static int s_old_topfill = 0;
1194#endif
1195 static int s_cClicks = 1;
1196 static BOOL s_fReleased = TRUE;
1197 static DWORD s_dwLastClickTime = 0;
1198 static BOOL s_fNextIsMiddle = FALSE;
1199
1200 static DWORD cButtons = 0; /* number of buttons supported */
1201
1202 const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
1203 const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
1204 const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
1205 const DWORD LEFT_RIGHT = LEFT | RIGHT;
1206
1207 int nButton;
1208
1209 if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons))
1210 cButtons = 2;
1211
1212 if (!g_fMouseAvail || !g_fMouseActive)
1213 {
1214 g_nMouseClick = -1;
1215 return FALSE;
1216 }
1217
1218 /* get a spurious MOUSE_EVENT immediately after receiving focus; ignore */
1219 if (g_fJustGotFocus)
1220 {
1221 g_fJustGotFocus = FALSE;
1222 return FALSE;
1223 }
1224
1225 /* unprocessed mouse click? */
1226 if (g_nMouseClick != -1)
1227 return TRUE;
1228
1229 nButton = -1;
1230 g_xMouse = pmer->dwMousePosition.X;
1231 g_yMouse = pmer->dwMousePosition.Y;
1232
1233 if (pmer->dwEventFlags == MOUSE_MOVED)
1234 {
1235 /* ignore MOUSE_MOVED events if (x, y) hasn't changed. (We get these
1236 * events even when the mouse moves only within a char cell.) */
1237 if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse)
1238 return FALSE;
1239 }
1240
1241 /* If no buttons are pressed... */
1242 if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0)
1243 {
1244 /* If the last thing returned was MOUSE_RELEASE, ignore this */
1245 if (s_fReleased)
1246 return FALSE;
1247
1248 nButton = MOUSE_RELEASE;
1249 s_fReleased = TRUE;
1250 }
1251 else /* one or more buttons pressed */
1252 {
1253 /* on a 2-button mouse, hold down left and right buttons
1254 * simultaneously to get MIDDLE. */
1255
1256 if (cButtons == 2 && s_nOldButton != MOUSE_DRAG)
1257 {
1258 DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
1259
1260 /* if either left or right button only is pressed, see if the
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02001261 * next mouse event has both of them pressed */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001262 if (dwLR == LEFT || dwLR == RIGHT)
1263 {
1264 for (;;)
1265 {
1266 /* wait a short time for next input event */
1267 if (WaitForSingleObject(g_hConIn, p_mouset / 3)
1268 != WAIT_OBJECT_0)
1269 break;
1270 else
1271 {
1272 DWORD cRecords = 0;
1273 INPUT_RECORD ir;
1274 MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
1275
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001276 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001277
1278 if (cRecords == 0 || ir.EventType != MOUSE_EVENT
1279 || !(pmer2->dwButtonState & LEFT_RIGHT))
1280 break;
1281 else
1282 {
1283 if (pmer2->dwEventFlags != MOUSE_MOVED)
1284 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001285 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001286
1287 return decode_mouse_event(pmer2);
1288 }
1289 else if (s_xOldMouse == pmer2->dwMousePosition.X &&
1290 s_yOldMouse == pmer2->dwMousePosition.Y)
1291 {
1292 /* throw away spurious mouse move */
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001293 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001294
1295 /* are there any more mouse events in queue? */
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001296 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001297
1298 if (cRecords==0 || ir.EventType != MOUSE_EVENT)
1299 break;
1300 }
1301 else
1302 break;
1303 }
1304 }
1305 }
1306 }
1307 }
1308
1309 if (s_fNextIsMiddle)
1310 {
1311 nButton = (pmer->dwEventFlags == MOUSE_MOVED)
1312 ? MOUSE_DRAG : MOUSE_MIDDLE;
1313 s_fNextIsMiddle = FALSE;
1314 }
1315 else if (cButtons == 2 &&
1316 ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
1317 {
1318 nButton = MOUSE_MIDDLE;
1319
1320 if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED)
1321 {
1322 s_fNextIsMiddle = TRUE;
1323 nButton = MOUSE_RELEASE;
1324 }
1325 }
1326 else if ((pmer->dwButtonState & LEFT) == LEFT)
1327 nButton = MOUSE_LEFT;
1328 else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
1329 nButton = MOUSE_MIDDLE;
1330 else if ((pmer->dwButtonState & RIGHT) == RIGHT)
1331 nButton = MOUSE_RIGHT;
1332
1333 if (! s_fReleased && ! s_fNextIsMiddle
1334 && nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG)
1335 return FALSE;
1336
1337 s_fReleased = s_fNextIsMiddle;
1338 }
1339
1340 if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK)
1341 {
1342 /* button pressed or released, without mouse moving */
1343 if (nButton != -1 && nButton != MOUSE_RELEASE)
1344 {
1345 DWORD dwCurrentTime = GetTickCount();
1346
1347 if (s_xOldMouse != g_xMouse
1348 || s_yOldMouse != g_yMouse
1349 || s_nOldButton != nButton
1350 || s_old_topline != curwin->w_topline
1351#ifdef FEAT_DIFF
1352 || s_old_topfill != curwin->w_topfill
1353#endif
1354 || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset)
1355 {
1356 s_cClicks = 1;
1357 }
1358 else if (++s_cClicks > 4)
1359 {
1360 s_cClicks = 1;
1361 }
1362
1363 s_dwLastClickTime = dwCurrentTime;
1364 }
1365 }
1366 else if (pmer->dwEventFlags == MOUSE_MOVED)
1367 {
1368 if (nButton != -1 && nButton != MOUSE_RELEASE)
1369 nButton = MOUSE_DRAG;
1370
1371 s_cClicks = 1;
1372 }
1373
1374 if (nButton == -1)
1375 return FALSE;
1376
1377 if (nButton != MOUSE_RELEASE)
1378 s_nOldButton = nButton;
1379
1380 g_nMouseClick = nButton;
1381
1382 if (pmer->dwControlKeyState & SHIFT_PRESSED)
1383 g_nMouseClick |= MOUSE_SHIFT;
1384 if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
1385 g_nMouseClick |= MOUSE_CTRL;
1386 if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
1387 g_nMouseClick |= MOUSE_ALT;
1388
1389 if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE)
1390 SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
1391
1392 /* only pass on interesting (i.e., different) mouse events */
1393 if (s_xOldMouse == g_xMouse
1394 && s_yOldMouse == g_yMouse
1395 && s_nOldMouseClick == g_nMouseClick)
1396 {
1397 g_nMouseClick = -1;
1398 return FALSE;
1399 }
1400
1401 s_xOldMouse = g_xMouse;
1402 s_yOldMouse = g_yMouse;
1403 s_old_topline = curwin->w_topline;
1404#ifdef FEAT_DIFF
1405 s_old_topfill = curwin->w_topfill;
1406#endif
1407 s_nOldMouseClick = g_nMouseClick;
1408
1409 return TRUE;
1410}
1411
1412# endif /* FEAT_GUI_W32 */
1413#endif /* FEAT_MOUSE */
1414
1415
1416#ifdef MCH_CURSOR_SHAPE
1417/*
1418 * Set the shape of the cursor.
1419 * 'thickness' can be from 1 (thin) to 99 (block)
1420 */
1421 static void
1422mch_set_cursor_shape(int thickness)
1423{
1424 CONSOLE_CURSOR_INFO ConsoleCursorInfo;
1425 ConsoleCursorInfo.dwSize = thickness;
1426 ConsoleCursorInfo.bVisible = s_cursor_visible;
1427
1428 SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo);
1429 if (s_cursor_visible)
1430 SetConsoleCursorPosition(g_hConOut, g_coord);
1431}
1432
1433 void
1434mch_update_cursor(void)
1435{
1436 int idx;
1437 int thickness;
1438
1439 /*
1440 * How the cursor is drawn depends on the current mode.
1441 */
1442 idx = get_shape_idx(FALSE);
1443
1444 if (shape_table[idx].shape == SHAPE_BLOCK)
1445 thickness = 99; /* 100 doesn't work on W95 */
1446 else
1447 thickness = shape_table[idx].percentage;
1448 mch_set_cursor_shape(thickness);
1449}
1450#endif
1451
1452#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1453/*
1454 * Handle FOCUS_EVENT.
1455 */
1456 static void
1457handle_focus_event(INPUT_RECORD ir)
1458{
1459 g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
1460 ui_focus_change((int)g_fJustGotFocus);
1461}
1462
1463/*
1464 * Wait until console input from keyboard or mouse is available,
1465 * or the time is up.
1466 * Return TRUE if something is available FALSE if not.
1467 */
1468 static int
1469WaitForChar(long msec)
1470{
1471 DWORD dwNow = 0, dwEndTime = 0;
1472 INPUT_RECORD ir;
1473 DWORD cRecords;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001474 WCHAR ch, ch2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001475
1476 if (msec > 0)
1477 /* Wait until the specified time has elapsed. */
1478 dwEndTime = GetTickCount() + msec;
1479 else if (msec < 0)
1480 /* Wait forever. */
1481 dwEndTime = INFINITE;
1482
1483 /* We need to loop until the end of the time period, because
1484 * we might get multiple unusable mouse events in that time.
1485 */
1486 for (;;)
1487 {
Bram Moolenaarca568ae2016-02-01 21:32:58 +01001488#ifdef MESSAGE_QUEUE
1489 parse_queued_messages();
1490#endif
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001491#ifdef FEAT_MZSCHEME
1492 mzvim_check_threads();
1493#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001494#ifdef FEAT_CLIENTSERVER
1495 serverProcessPendingMessages();
1496#endif
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001497
1498#ifdef FEAT_CHANNEL
Bram Moolenaar4d919d72016-02-05 22:36:41 +01001499 if (handle_channel_event())
1500 return TRUE;
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001501#endif
1502
Bram Moolenaar071d4272004-06-13 20:20:40 +00001503 if (0
1504#ifdef FEAT_MOUSE
1505 || g_nMouseClick != -1
1506#endif
1507#ifdef FEAT_CLIENTSERVER
1508 || input_available()
1509#endif
1510 )
1511 return TRUE;
1512
1513 if (msec > 0)
1514 {
Bram Moolenaarb7512b72013-08-10 12:45:09 +02001515 /* If the specified wait time has passed, return. Beware that
1516 * GetTickCount() may wrap around (overflow). */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001517 dwNow = GetTickCount();
Bram Moolenaarb7512b72013-08-10 12:45:09 +02001518 if ((int)(dwNow - dwEndTime) >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001519 break;
1520 }
1521 if (msec != 0)
1522 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001523 DWORD dwWaitTime = dwEndTime - dwNow;
1524
1525#ifdef FEAT_MZSCHEME
1526 if (mzthreads_allowed() && p_mzq > 0
1527 && (msec < 0 || (long)dwWaitTime > p_mzq))
1528 dwWaitTime = p_mzq; /* don't wait longer than 'mzquantum' */
1529#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001530#ifdef FEAT_CLIENTSERVER
1531 /* Wait for either an event on the console input or a message in
1532 * the client-server window. */
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +02001533 if (msg_wait_for_multiple_objects(1, &g_hConIn, FALSE,
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001534 dwWaitTime, QS_SENDMESSAGE) != WAIT_OBJECT_0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001535#else
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +02001536 if (wait_for_single_object(g_hConIn, dwWaitTime) != WAIT_OBJECT_0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001537#endif
1538 continue;
1539 }
1540
1541 cRecords = 0;
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001542 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001543
1544#ifdef FEAT_MBYTE_IME
1545 if (State & CMDLINE && msg_row == Rows - 1)
1546 {
1547 CONSOLE_SCREEN_BUFFER_INFO csbi;
1548
1549 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1550 {
1551 if (csbi.dwCursorPosition.Y != msg_row)
1552 {
1553 /* The screen is now messed up, must redraw the
1554 * command line and later all the windows. */
1555 redraw_all_later(CLEAR);
1556 cmdline_row -= (msg_row - csbi.dwCursorPosition.Y);
1557 redrawcmd();
1558 }
1559 }
1560 }
1561#endif
1562
1563 if (cRecords > 0)
1564 {
1565 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
1566 {
1567#ifdef FEAT_MBYTE_IME
1568 /* Windows IME sends two '\n's with only one 'ENTER'. First:
1569 * wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001570 if (ir.Event.KeyEvent.UChar == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001571 && ir.Event.KeyEvent.wVirtualKeyCode == 13)
1572 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001573 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001574 continue;
1575 }
1576#endif
1577 if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
1578 NULL, FALSE))
1579 return TRUE;
1580 }
1581
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001582 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001583
1584 if (ir.EventType == FOCUS_EVENT)
1585 handle_focus_event(ir);
1586 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1587 shell_resized();
1588#ifdef FEAT_MOUSE
1589 else if (ir.EventType == MOUSE_EVENT
1590 && decode_mouse_event(&ir.Event.MouseEvent))
1591 return TRUE;
1592#endif
1593 }
1594 else if (msec == 0)
1595 break;
1596 }
1597
1598#ifdef FEAT_CLIENTSERVER
1599 /* Something might have been received while we were waiting. */
1600 if (input_available())
1601 return TRUE;
1602#endif
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001603
Bram Moolenaar071d4272004-06-13 20:20:40 +00001604 return FALSE;
1605}
1606
1607#ifndef FEAT_GUI_MSWIN
1608/*
1609 * return non-zero if a character is available
1610 */
1611 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001612mch_char_avail(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001613{
1614 return WaitForChar(0L);
1615}
1616#endif
1617
1618/*
1619 * Create the console input. Used when reading stdin doesn't work.
1620 */
1621 static void
1622create_conin(void)
1623{
1624 g_hConIn = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
1625 FILE_SHARE_READ|FILE_SHARE_WRITE,
1626 (LPSECURITY_ATTRIBUTES) NULL,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001627 OPEN_EXISTING, 0, (HANDLE)NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001628 did_create_conin = TRUE;
1629}
1630
1631/*
1632 * Get a keystroke or a mouse event
1633 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001634 static WCHAR
1635tgetch(int *pmodifiers, WCHAR *pch2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001636{
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001637 WCHAR ch;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001638
1639 for (;;)
1640 {
1641 INPUT_RECORD ir;
1642 DWORD cRecords = 0;
1643
1644#ifdef FEAT_CLIENTSERVER
1645 (void)WaitForChar(-1L);
1646 if (input_available())
1647 return 0;
1648# ifdef FEAT_MOUSE
1649 if (g_nMouseClick != -1)
1650 return 0;
1651# endif
1652#endif
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001653 if (read_console_input(g_hConIn, &ir, 1, &cRecords) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001654 {
1655 if (did_create_conin)
1656 read_error_exit();
1657 create_conin();
1658 continue;
1659 }
1660
1661 if (ir.EventType == KEY_EVENT)
1662 {
1663 if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2,
1664 pmodifiers, TRUE))
1665 return ch;
1666 }
1667 else if (ir.EventType == FOCUS_EVENT)
1668 handle_focus_event(ir);
1669 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1670 shell_resized();
1671#ifdef FEAT_MOUSE
1672 else if (ir.EventType == MOUSE_EVENT)
1673 {
1674 if (decode_mouse_event(&ir.Event.MouseEvent))
1675 return 0;
1676 }
1677#endif
1678 }
1679}
1680#endif /* !FEAT_GUI_W32 */
1681
1682
1683/*
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02001684 * mch_inchar(): low-level input function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001685 * Get one or more characters from the keyboard or the mouse.
1686 * If time == 0, do not wait for characters.
1687 * If time == n, wait a short time for characters.
1688 * If time == -1, wait forever for characters.
1689 * Returns the number of characters read into buf.
1690 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00001691/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00001692 int
1693mch_inchar(
1694 char_u *buf,
1695 int maxlen,
1696 long time,
1697 int tb_change_cnt)
1698{
1699#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1700
1701 int len;
1702 int c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001703#define TYPEAHEADLEN 20
1704 static char_u typeahead[TYPEAHEADLEN]; /* previously typed bytes. */
1705 static int typeaheadlen = 0;
1706
1707 /* First use any typeahead that was kept because "buf" was too small. */
1708 if (typeaheadlen > 0)
1709 goto theend;
1710
1711#ifdef FEAT_SNIFF
1712 if (want_sniff_request)
1713 {
1714 if (sniff_request_waiting)
1715 {
1716 /* return K_SNIFF */
1717 typeahead[typeaheadlen++] = CSI;
1718 typeahead[typeaheadlen++] = (char_u)KS_EXTRA;
1719 typeahead[typeaheadlen++] = (char_u)KE_SNIFF;
1720 sniff_request_waiting = 0;
1721 want_sniff_request = 0;
1722 goto theend;
1723 }
1724 else if (time < 0 || time > 250)
1725 {
1726 /* don't wait too long, a request might be pending */
1727 time = 250;
1728 }
1729 }
1730#endif
1731
1732 if (time >= 0)
1733 {
1734 if (!WaitForChar(time)) /* no character available */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001735 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001736 }
1737 else /* time == -1, wait forever */
1738 {
1739 mch_set_winsize_now(); /* Allow winsize changes from now on */
1740
Bram Moolenaar3918c952005-03-15 22:34:55 +00001741 /*
1742 * If there is no character available within 2 seconds (default)
1743 * write the autoscript file to disk. Or cause the CursorHold event
1744 * to be triggered.
1745 */
1746 if (!WaitForChar(p_ut))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001747 {
1748#ifdef FEAT_AUTOCMD
Bram Moolenaard35f9712005-12-18 22:02:33 +00001749 if (trigger_cursorhold() && maxlen >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001750 {
Bram Moolenaar3918c952005-03-15 22:34:55 +00001751 buf[0] = K_SPECIAL;
1752 buf[1] = KS_EXTRA;
1753 buf[2] = (int)KE_CURSORHOLD;
1754 return 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001755 }
1756#endif
Bram Moolenaar702517d2005-06-27 22:34:07 +00001757 before_blocking();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001758 }
1759 }
1760
1761 /*
1762 * Try to read as many characters as there are, until the buffer is full.
1763 */
1764
1765 /* we will get at least one key. Get more if they are available. */
1766 g_fCBrkPressed = FALSE;
1767
1768#ifdef MCH_WRITE_DUMP
1769 if (fdDump)
1770 fputc('[', fdDump);
1771#endif
1772
1773 /* Keep looping until there is something in the typeahead buffer and more
1774 * to get and still room in the buffer (up to two bytes for a char and
1775 * three bytes for a modifier). */
1776 while ((typeaheadlen == 0 || WaitForChar(0L))
1777 && typeaheadlen + 5 <= TYPEAHEADLEN)
1778 {
1779 if (typebuf_changed(tb_change_cnt))
1780 {
1781 /* "buf" may be invalid now if a client put something in the
1782 * typeahead buffer and "buf" is in the typeahead buffer. */
1783 typeaheadlen = 0;
1784 break;
1785 }
1786#ifdef FEAT_MOUSE
1787 if (g_nMouseClick != -1)
1788 {
1789# ifdef MCH_WRITE_DUMP
1790 if (fdDump)
1791 fprintf(fdDump, "{%02x @ %d, %d}",
1792 g_nMouseClick, g_xMouse, g_yMouse);
1793# endif
1794 typeahead[typeaheadlen++] = ESC + 128;
1795 typeahead[typeaheadlen++] = 'M';
1796 typeahead[typeaheadlen++] = g_nMouseClick;
1797 typeahead[typeaheadlen++] = g_xMouse + '!';
1798 typeahead[typeaheadlen++] = g_yMouse + '!';
1799 g_nMouseClick = -1;
1800 }
1801 else
1802#endif
1803 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001804 WCHAR ch2 = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001805 int modifiers = 0;
1806
1807 c = tgetch(&modifiers, &ch2);
1808
1809 if (typebuf_changed(tb_change_cnt))
1810 {
1811 /* "buf" may be invalid now if a client put something in the
1812 * typeahead buffer and "buf" is in the typeahead buffer. */
1813 typeaheadlen = 0;
1814 break;
1815 }
1816
1817 if (c == Ctrl_C && ctrl_c_interrupts)
1818 {
1819#if defined(FEAT_CLIENTSERVER)
1820 trash_input_buf();
1821#endif
1822 got_int = TRUE;
1823 }
1824
1825#ifdef FEAT_MOUSE
1826 if (g_nMouseClick == -1)
1827#endif
1828 {
1829 int n = 1;
Bram Moolenaar45500912014-07-09 20:51:07 +02001830 int conv = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001831
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001832#ifdef FEAT_MBYTE
1833 if (ch2 == NUL)
1834 {
1835 int i;
1836 char_u *p;
1837 WCHAR ch[2];
1838
1839 ch[0] = c;
1840 if (c >= 0xD800 && c <= 0xDBFF) /* High surrogate */
1841 {
1842 ch[1] = tgetch(&modifiers, &ch2);
1843 n++;
1844 }
1845 p = utf16_to_enc(ch, &n);
1846 if (p != NULL)
1847 {
1848 for (i = 0; i < n; i++)
1849 typeahead[typeaheadlen + i] = p[i];
1850 vim_free(p);
1851 }
1852 }
1853 else
1854#endif
1855 typeahead[typeaheadlen] = c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001856 if (ch2 != NUL)
1857 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001858 typeahead[typeaheadlen + n] = 3;
1859 typeahead[typeaheadlen + n + 1] = (char_u)ch2;
Bram Moolenaar45500912014-07-09 20:51:07 +02001860 n += 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001862
Bram Moolenaar45500912014-07-09 20:51:07 +02001863 if (conv)
1864 {
1865 char_u *p = typeahead + typeaheadlen;
Bram Moolenaar45500912014-07-09 20:51:07 +02001866
Bram Moolenaar1ec4dd42015-01-20 19:39:35 +01001867 if (*p != K_NUL)
Bram Moolenaar45500912014-07-09 20:51:07 +02001868 {
Bram Moolenaar1ec4dd42015-01-20 19:39:35 +01001869 char_u *e = typeahead + TYPEAHEADLEN;
1870
1871 while (*p && p < e)
Bram Moolenaar45500912014-07-09 20:51:07 +02001872 {
Bram Moolenaar1ec4dd42015-01-20 19:39:35 +01001873 if (*p == K_NUL)
1874 {
1875 ++p;
1876 mch_memmove(p + 1, p, ((size_t)(e - p)) - 1);
1877 *p = 3;
1878 ++n;
1879 }
Bram Moolenaar45500912014-07-09 20:51:07 +02001880 ++p;
Bram Moolenaar45500912014-07-09 20:51:07 +02001881 }
Bram Moolenaar45500912014-07-09 20:51:07 +02001882 }
1883 }
1884
Bram Moolenaar071d4272004-06-13 20:20:40 +00001885 /* Use the ALT key to set the 8th bit of the character
1886 * when it's one byte, the 8th bit isn't set yet and not
1887 * using a double-byte encoding (would become a lead
1888 * byte). */
1889 if ((modifiers & MOD_MASK_ALT)
1890 && n == 1
1891 && (typeahead[typeaheadlen] & 0x80) == 0
1892#ifdef FEAT_MBYTE
1893 && !enc_dbcs
1894#endif
1895 )
1896 {
Bram Moolenaar85a3e5c2007-11-20 16:22:16 +00001897#ifdef FEAT_MBYTE
1898 n = (*mb_char2bytes)(typeahead[typeaheadlen] | 0x80,
1899 typeahead + typeaheadlen);
1900#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001901 typeahead[typeaheadlen] |= 0x80;
Bram Moolenaar85a3e5c2007-11-20 16:22:16 +00001902#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001903 modifiers &= ~MOD_MASK_ALT;
1904 }
1905
1906 if (modifiers != 0)
1907 {
1908 /* Prepend modifiers to the character. */
1909 mch_memmove(typeahead + typeaheadlen + 3,
1910 typeahead + typeaheadlen, n);
1911 typeahead[typeaheadlen++] = K_SPECIAL;
1912 typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
1913 typeahead[typeaheadlen++] = modifiers;
1914 }
1915
1916 typeaheadlen += n;
1917
1918#ifdef MCH_WRITE_DUMP
1919 if (fdDump)
1920 fputc(c, fdDump);
1921#endif
1922 }
1923 }
1924 }
1925
1926#ifdef MCH_WRITE_DUMP
1927 if (fdDump)
1928 {
1929 fputs("]\n", fdDump);
1930 fflush(fdDump);
1931 }
1932#endif
1933
Bram Moolenaar071d4272004-06-13 20:20:40 +00001934theend:
1935 /* Move typeahead to "buf", as much as fits. */
1936 len = 0;
1937 while (len < maxlen && typeaheadlen > 0)
1938 {
1939 buf[len++] = typeahead[0];
1940 mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
1941 }
1942 return len;
1943
1944#else /* FEAT_GUI_W32 */
1945 return 0;
1946#endif /* FEAT_GUI_W32 */
1947}
1948
Bram Moolenaar82881492012-11-20 16:53:39 +01001949#ifndef PROTO
1950# ifndef __MINGW32__
1951# include <shellapi.h> /* required for FindExecutable() */
1952# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001953#endif
1954
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001955/*
1956 * Return TRUE if "name" is in $PATH.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00001957 * TODO: Should somehow check if it's really executable.
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001958 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001959 static int
Bram Moolenaarc7f02552014-04-01 21:00:59 +02001960executable_exists(char *name, char_u **path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001961{
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001962 char *dum;
1963 char fname[_MAX_PATH];
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02001964 char *curpath, *newpath;
1965 long n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001966
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001967#ifdef FEAT_MBYTE
1968 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001969 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001970 WCHAR *p = enc_to_utf16((char_u *)name, NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001971 WCHAR fnamew[_MAX_PATH];
1972 WCHAR *dumw;
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02001973 WCHAR *wcurpath, *wnewpath;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001974
1975 if (p != NULL)
1976 {
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02001977 wcurpath = _wgetenv(L"PATH");
1978 wnewpath = (WCHAR*)alloc((unsigned)(wcslen(wcurpath) + 3)
1979 * sizeof(WCHAR));
1980 if (wnewpath == NULL)
1981 return FALSE;
1982 wcscpy(wnewpath, L".;");
1983 wcscat(wnewpath, wcurpath);
1984 n = (long)SearchPathW(wnewpath, p, NULL, _MAX_PATH, fnamew, &dumw);
1985 vim_free(wnewpath);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001986 vim_free(p);
1987 if (n > 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
1988 {
1989 if (n == 0)
1990 return FALSE;
1991 if (GetFileAttributesW(fnamew) & FILE_ATTRIBUTE_DIRECTORY)
1992 return FALSE;
Bram Moolenaarc7f02552014-04-01 21:00:59 +02001993 if (path != NULL)
1994 *path = utf16_to_enc(fnamew, NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001995 return TRUE;
1996 }
1997 /* Retry with non-wide function (for Windows 98). */
1998 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001999 }
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002000#endif
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02002001
2002 curpath = getenv("PATH");
2003 newpath = (char*)alloc((unsigned)(STRLEN(curpath) + 3));
2004 if (newpath == NULL)
2005 return FALSE;
2006 STRCPY(newpath, ".;");
2007 STRCAT(newpath, curpath);
2008 n = (long)SearchPath(newpath, name, NULL, _MAX_PATH, fname, &dum);
2009 vim_free(newpath);
2010 if (n == 0)
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002011 return FALSE;
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002012 if (mch_isdir((char_u *)fname))
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002013 return FALSE;
Bram Moolenaarc7f02552014-04-01 21:00:59 +02002014 if (path != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002015 *path = vim_strsave((char_u *)fname);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002016 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002017}
2018
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002019#if ((defined(__MINGW32__) || defined (__CYGWIN32__)) && \
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02002020 __MSVCRT_VERSION__ >= 0x800) || (defined(_MSC_VER) && _MSC_VER >= 1400)
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002021/*
2022 * Bad parameter handler.
2023 *
2024 * Certain MS CRT functions will intentionally crash when passed invalid
2025 * parameters to highlight possible security holes. Setting this function as
2026 * the bad parameter handler will prevent the crash.
2027 *
2028 * In debug builds the parameters contain CRT information that might help track
2029 * down the source of a problem, but in non-debug builds the arguments are all
2030 * NULL/0. Debug builds will also produce assert dialogs from the CRT, it is
2031 * worth allowing these to make debugging of issues easier.
2032 */
2033 static void
2034bad_param_handler(const wchar_t *expression,
2035 const wchar_t *function,
2036 const wchar_t *file,
2037 unsigned int line,
2038 uintptr_t pReserved)
2039{
2040}
2041
2042# define SET_INVALID_PARAM_HANDLER \
2043 ((void)_set_invalid_parameter_handler(bad_param_handler))
2044#else
2045# define SET_INVALID_PARAM_HANDLER
2046#endif
2047
Bram Moolenaar071d4272004-06-13 20:20:40 +00002048#ifdef FEAT_GUI_W32
2049
2050/*
2051 * GUI version of mch_init().
2052 */
2053 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002054mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002055{
2056#ifndef __MINGW32__
2057 extern int _fmode;
2058#endif
2059
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002060 /* Silently handle invalid parameters to CRT functions */
2061 SET_INVALID_PARAM_HANDLER;
2062
Bram Moolenaar071d4272004-06-13 20:20:40 +00002063 /* Let critical errors result in a failure, not in a dialog box. Required
2064 * for the timestamp test to work on removed floppies. */
2065 SetErrorMode(SEM_FAILCRITICALERRORS);
2066
2067 _fmode = O_BINARY; /* we do our own CR-LF translation */
2068
2069 /* Specify window size. Is there a place to get the default from? */
2070 Rows = 25;
2071 Columns = 80;
2072
2073 /* Look for 'vimrun' */
2074 if (!gui_is_win32s())
2075 {
2076 char_u vimrun_location[_MAX_PATH + 4];
2077
2078 /* First try in same directory as gvim.exe */
2079 STRCPY(vimrun_location, exe_name);
2080 STRCPY(gettail(vimrun_location), "vimrun.exe");
2081 if (mch_getperm(vimrun_location) >= 0)
2082 {
2083 if (*skiptowhite(vimrun_location) != NUL)
2084 {
2085 /* Enclose path with white space in double quotes. */
2086 mch_memmove(vimrun_location + 1, vimrun_location,
2087 STRLEN(vimrun_location) + 1);
2088 *vimrun_location = '"';
2089 STRCPY(gettail(vimrun_location), "vimrun\" ");
2090 }
2091 else
2092 STRCPY(gettail(vimrun_location), "vimrun ");
2093
2094 vimrun_path = (char *)vim_strsave(vimrun_location);
2095 s_dont_use_vimrun = FALSE;
2096 }
Bram Moolenaarc7f02552014-04-01 21:00:59 +02002097 else if (executable_exists("vimrun.exe", NULL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002098 s_dont_use_vimrun = FALSE;
2099
2100 /* Don't give the warning for a missing vimrun.exe right now, but only
2101 * when vimrun was supposed to be used. Don't bother people that do
2102 * not need vimrun.exe. */
2103 if (s_dont_use_vimrun)
2104 need_vimrun_warning = TRUE;
2105 }
2106
2107 /*
2108 * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
2109 * Otherwise the default "findstr /n" is used.
2110 */
Bram Moolenaarc7f02552014-04-01 21:00:59 +02002111 if (!executable_exists("findstr.exe", NULL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112 set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
2113
2114#ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002115 win_clip_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002116#endif
2117}
2118
2119
2120#else /* FEAT_GUI_W32 */
2121
2122#define SRWIDTH(sr) ((sr).Right - (sr).Left + 1)
2123#define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1)
2124
2125/*
2126 * ClearConsoleBuffer()
2127 * Description:
2128 * Clears the entire contents of the console screen buffer, using the
2129 * specified attribute.
2130 * Returns:
2131 * TRUE on success
2132 */
2133 static BOOL
2134ClearConsoleBuffer(WORD wAttribute)
2135{
2136 CONSOLE_SCREEN_BUFFER_INFO csbi;
2137 COORD coord;
2138 DWORD NumCells, dummy;
2139
2140 if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2141 return FALSE;
2142
2143 NumCells = csbi.dwSize.X * csbi.dwSize.Y;
2144 coord.X = 0;
2145 coord.Y = 0;
2146 if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells,
2147 coord, &dummy))
2148 {
2149 return FALSE;
2150 }
2151 if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells,
2152 coord, &dummy))
2153 {
2154 return FALSE;
2155 }
2156
2157 return TRUE;
2158}
2159
2160/*
2161 * FitConsoleWindow()
2162 * Description:
2163 * Checks if the console window will fit within given buffer dimensions.
2164 * Also, if requested, will shrink the window to fit.
2165 * Returns:
2166 * TRUE on success
2167 */
2168 static BOOL
2169FitConsoleWindow(
2170 COORD dwBufferSize,
2171 BOOL WantAdjust)
2172{
2173 CONSOLE_SCREEN_BUFFER_INFO csbi;
2174 COORD dwWindowSize;
2175 BOOL NeedAdjust = FALSE;
2176
2177 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2178 {
2179 /*
2180 * A buffer resize will fail if the current console window does
2181 * not lie completely within that buffer. To avoid this, we might
2182 * have to move and possibly shrink the window.
2183 */
2184 if (csbi.srWindow.Right >= dwBufferSize.X)
2185 {
2186 dwWindowSize.X = SRWIDTH(csbi.srWindow);
2187 if (dwWindowSize.X > dwBufferSize.X)
2188 dwWindowSize.X = dwBufferSize.X;
2189 csbi.srWindow.Right = dwBufferSize.X - 1;
2190 csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X;
2191 NeedAdjust = TRUE;
2192 }
2193 if (csbi.srWindow.Bottom >= dwBufferSize.Y)
2194 {
2195 dwWindowSize.Y = SRHEIGHT(csbi.srWindow);
2196 if (dwWindowSize.Y > dwBufferSize.Y)
2197 dwWindowSize.Y = dwBufferSize.Y;
2198 csbi.srWindow.Bottom = dwBufferSize.Y - 1;
2199 csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y;
2200 NeedAdjust = TRUE;
2201 }
2202 if (NeedAdjust && WantAdjust)
2203 {
2204 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow))
2205 return FALSE;
2206 }
2207 return TRUE;
2208 }
2209
2210 return FALSE;
2211}
2212
2213typedef struct ConsoleBufferStruct
2214{
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002215 BOOL IsValid;
2216 CONSOLE_SCREEN_BUFFER_INFO Info;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002217 PCHAR_INFO Buffer;
2218 COORD BufferSize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002219} ConsoleBuffer;
2220
2221/*
2222 * SaveConsoleBuffer()
2223 * Description:
2224 * Saves important information about the console buffer, including the
2225 * actual buffer contents. The saved information is suitable for later
2226 * restoration by RestoreConsoleBuffer().
2227 * Returns:
2228 * TRUE if all information was saved; FALSE otherwise
2229 * If FALSE, still sets cb->IsValid if buffer characteristics were saved.
2230 */
2231 static BOOL
2232SaveConsoleBuffer(
2233 ConsoleBuffer *cb)
2234{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002235 DWORD NumCells;
2236 COORD BufferCoord;
2237 SMALL_RECT ReadRegion;
2238 WORD Y, Y_incr;
2239
Bram Moolenaar071d4272004-06-13 20:20:40 +00002240 if (cb == NULL)
2241 return FALSE;
2242
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002243 if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002244 {
2245 cb->IsValid = FALSE;
2246 return FALSE;
2247 }
2248 cb->IsValid = TRUE;
2249
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002250 /*
2251 * Allocate a buffer large enough to hold the entire console screen
2252 * buffer. If this ConsoleBuffer structure has already been initialized
2253 * with a buffer of the correct size, then just use that one.
2254 */
2255 if (!cb->IsValid || cb->Buffer == NULL ||
2256 cb->BufferSize.X != cb->Info.dwSize.X ||
2257 cb->BufferSize.Y != cb->Info.dwSize.Y)
2258 {
2259 cb->BufferSize.X = cb->Info.dwSize.X;
2260 cb->BufferSize.Y = cb->Info.dwSize.Y;
2261 NumCells = cb->BufferSize.X * cb->BufferSize.Y;
2262 vim_free(cb->Buffer);
2263 cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
2264 if (cb->Buffer == NULL)
2265 return FALSE;
2266 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002267
2268 /*
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002269 * We will now copy the console screen buffer into our buffer.
2270 * ReadConsoleOutput() seems to be limited as far as how much you
2271 * can read at a time. Empirically, this number seems to be about
2272 * 12000 cells (rows * columns). Start at position (0, 0) and copy
2273 * in chunks until it is all copied. The chunks will all have the
2274 * same horizontal characteristics, so initialize them now. The
2275 * height of each chunk will be (12000 / width).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002276 */
Bram Moolenaar61594242015-09-01 20:23:37 +02002277 BufferCoord.X = 0;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002278 ReadRegion.Left = 0;
2279 ReadRegion.Right = cb->Info.dwSize.X - 1;
2280 Y_incr = 12000 / cb->Info.dwSize.X;
2281 for (Y = 0; Y < cb->BufferSize.Y; Y += Y_incr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002282 {
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002283 /*
2284 * Read into position (0, Y) in our buffer.
2285 */
2286 BufferCoord.Y = Y;
2287 /*
2288 * Read the region whose top left corner is (0, Y) and whose bottom
2289 * right corner is (width - 1, Y + Y_incr - 1). This should define
2290 * a region of size width by Y_incr. Don't worry if this region is
2291 * too large for the remaining buffer; it will be cropped.
2292 */
2293 ReadRegion.Top = Y;
2294 ReadRegion.Bottom = Y + Y_incr - 1;
2295 if (!ReadConsoleOutput(g_hConOut, /* output handle */
2296 cb->Buffer, /* our buffer */
2297 cb->BufferSize, /* dimensions of our buffer */
2298 BufferCoord, /* offset in our buffer */
2299 &ReadRegion)) /* region to save */
2300 {
2301 vim_free(cb->Buffer);
2302 cb->Buffer = NULL;
2303 return FALSE;
2304 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002305 }
2306
2307 return TRUE;
2308}
2309
2310/*
2311 * RestoreConsoleBuffer()
2312 * Description:
2313 * Restores important information about the console buffer, including the
2314 * actual buffer contents, if desired. The information to restore is in
2315 * the same format used by SaveConsoleBuffer().
2316 * Returns:
2317 * TRUE on success
2318 */
2319 static BOOL
2320RestoreConsoleBuffer(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002321 ConsoleBuffer *cb,
2322 BOOL RestoreScreen)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002323{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002324 COORD BufferCoord;
2325 SMALL_RECT WriteRegion;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002326
2327 if (cb == NULL || !cb->IsValid)
2328 return FALSE;
2329
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002330 /*
2331 * Before restoring the buffer contents, clear the current buffer, and
2332 * restore the cursor position and window information. Doing this now
2333 * prevents old buffer contents from "flashing" onto the screen.
2334 */
2335 if (RestoreScreen)
2336 ClearConsoleBuffer(cb->Info.wAttributes);
2337
2338 FitConsoleWindow(cb->Info.dwSize, TRUE);
2339 if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
2340 return FALSE;
2341 if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
2342 return FALSE;
2343
2344 if (!RestoreScreen)
2345 {
2346 /*
2347 * No need to restore the screen buffer contents, so we're done.
2348 */
2349 return TRUE;
2350 }
2351
2352 if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
2353 return FALSE;
2354 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
2355 return FALSE;
2356
2357 /*
2358 * Restore the screen buffer contents.
2359 */
2360 if (cb->Buffer != NULL)
2361 {
2362 BufferCoord.X = 0;
2363 BufferCoord.Y = 0;
2364 WriteRegion.Left = 0;
2365 WriteRegion.Top = 0;
2366 WriteRegion.Right = cb->Info.dwSize.X - 1;
2367 WriteRegion.Bottom = cb->Info.dwSize.Y - 1;
2368 if (!WriteConsoleOutput(g_hConOut, /* output handle */
2369 cb->Buffer, /* our buffer */
2370 cb->BufferSize, /* dimensions of our buffer */
2371 BufferCoord, /* offset in our buffer */
2372 &WriteRegion)) /* region to restore */
2373 {
2374 return FALSE;
2375 }
2376 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002377
2378 return TRUE;
2379}
2380
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002381#define FEAT_RESTORE_ORIG_SCREEN
2382#ifdef FEAT_RESTORE_ORIG_SCREEN
2383static ConsoleBuffer g_cbOrig = { 0 };
2384#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002385static ConsoleBuffer g_cbNonTermcap = { 0 };
2386static ConsoleBuffer g_cbTermcap = { 0 };
2387
2388#ifdef FEAT_TITLE
2389#ifdef __BORLANDC__
2390typedef HWND (__stdcall *GETCONSOLEWINDOWPROC)(VOID);
2391#else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002392typedef HWND (WINAPI *GETCONSOLEWINDOWPROC)(VOID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002393#endif
2394char g_szOrigTitle[256] = { 0 };
2395HWND g_hWnd = NULL; /* also used in os_mswin.c */
2396static HICON g_hOrigIconSmall = NULL;
2397static HICON g_hOrigIcon = NULL;
2398static HICON g_hVimIcon = NULL;
2399static BOOL g_fCanChangeIcon = FALSE;
2400
2401/* ICON* are not defined in VC++ 4.0 */
2402#ifndef ICON_SMALL
2403#define ICON_SMALL 0
2404#endif
2405#ifndef ICON_BIG
2406#define ICON_BIG 1
2407#endif
2408/*
2409 * GetConsoleIcon()
2410 * Description:
2411 * Attempts to retrieve the small icon and/or the big icon currently in
2412 * use by a given window.
2413 * Returns:
2414 * TRUE on success
2415 */
2416 static BOOL
2417GetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002418 HWND hWnd,
2419 HICON *phIconSmall,
2420 HICON *phIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002421{
2422 if (hWnd == NULL)
2423 return FALSE;
2424
2425 if (phIconSmall != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002426 *phIconSmall = (HICON)SendMessage(hWnd, WM_GETICON,
2427 (WPARAM)ICON_SMALL, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002428 if (phIcon != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002429 *phIcon = (HICON)SendMessage(hWnd, WM_GETICON,
2430 (WPARAM)ICON_BIG, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431 return TRUE;
2432}
2433
2434/*
2435 * SetConsoleIcon()
2436 * Description:
2437 * Attempts to change the small icon and/or the big icon currently in
2438 * use by a given window.
2439 * Returns:
2440 * TRUE on success
2441 */
2442 static BOOL
2443SetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002444 HWND hWnd,
2445 HICON hIconSmall,
2446 HICON hIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002447{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002448 if (hWnd == NULL)
2449 return FALSE;
2450
2451 if (hIconSmall != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002452 SendMessage(hWnd, WM_SETICON,
2453 (WPARAM)ICON_SMALL, (LPARAM)hIconSmall);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002454 if (hIcon != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002455 SendMessage(hWnd, WM_SETICON,
2456 (WPARAM)ICON_BIG, (LPARAM) hIcon);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 return TRUE;
2458}
2459
2460/*
2461 * SaveConsoleTitleAndIcon()
2462 * Description:
2463 * Saves the current console window title in g_szOrigTitle, for later
2464 * restoration. Also, attempts to obtain a handle to the console window,
2465 * and use it to save the small and big icons currently in use by the
2466 * console window. This is not always possible on some versions of Windows;
2467 * nor is it possible when running Vim remotely using Telnet (since the
2468 * console window the user sees is owned by a remote process).
2469 */
2470 static void
2471SaveConsoleTitleAndIcon(void)
2472{
2473 GETCONSOLEWINDOWPROC GetConsoleWindowProc;
2474
2475 /* Save the original title. */
2476 if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle)))
2477 return;
2478
2479 /*
2480 * Obtain a handle to the console window using GetConsoleWindow() from
2481 * KERNEL32.DLL; we need to handle in order to change the window icon.
2482 * This function only exists on NT-based Windows, starting with Windows
2483 * 2000. On older operating systems, we can't change the window icon
2484 * anyway.
2485 */
2486 if ((GetConsoleWindowProc = (GETCONSOLEWINDOWPROC)
2487 GetProcAddress(GetModuleHandle("KERNEL32.DLL"),
2488 "GetConsoleWindow")) != NULL)
2489 {
2490 g_hWnd = (*GetConsoleWindowProc)();
2491 }
2492 if (g_hWnd == NULL)
2493 return;
2494
2495 /* Save the original console window icon. */
2496 GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon);
2497 if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL)
2498 return;
2499
2500 /* Extract the first icon contained in the Vim executable. */
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02002501 if (mch_icon_load((HANDLE *)&g_hVimIcon) == FAIL || g_hVimIcon == NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002502 g_hVimIcon = ExtractIcon(NULL, (LPCSTR)exe_name, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002503 if (g_hVimIcon != NULL)
2504 g_fCanChangeIcon = TRUE;
2505}
2506#endif
2507
2508static int g_fWindInitCalled = FALSE;
2509static int g_fTermcapMode = FALSE;
2510static CONSOLE_CURSOR_INFO g_cci;
2511static DWORD g_cmodein = 0;
2512static DWORD g_cmodeout = 0;
2513
2514/*
2515 * non-GUI version of mch_init().
2516 */
2517 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002518mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002519{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002520#ifndef FEAT_RESTORE_ORIG_SCREEN
2521 CONSOLE_SCREEN_BUFFER_INFO csbi;
2522#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002523#ifndef __MINGW32__
2524 extern int _fmode;
2525#endif
2526
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002527 /* Silently handle invalid parameters to CRT functions */
2528 SET_INVALID_PARAM_HANDLER;
2529
Bram Moolenaar071d4272004-06-13 20:20:40 +00002530 /* Let critical errors result in a failure, not in a dialog box. Required
2531 * for the timestamp test to work on removed floppies. */
2532 SetErrorMode(SEM_FAILCRITICALERRORS);
2533
2534 _fmode = O_BINARY; /* we do our own CR-LF translation */
2535 out_flush();
2536
2537 /* Obtain handles for the standard Console I/O devices */
2538 if (read_cmd_fd == 0)
2539 g_hConIn = GetStdHandle(STD_INPUT_HANDLE);
2540 else
2541 create_conin();
2542 g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
2543
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002544#ifdef FEAT_RESTORE_ORIG_SCREEN
2545 /* Save the initial console buffer for later restoration */
2546 SaveConsoleBuffer(&g_cbOrig);
2547 g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
2548#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002549 /* Get current text attributes */
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002550 GetConsoleScreenBufferInfo(g_hConOut, &csbi);
2551 g_attrCurrent = g_attrDefault = csbi.wAttributes;
2552#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002553 if (cterm_normal_fg_color == 0)
2554 cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
2555 if (cterm_normal_bg_color == 0)
2556 cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
2557
2558 /* set termcap codes to current text attributes */
2559 update_tcap(g_attrCurrent);
2560
2561 GetConsoleCursorInfo(g_hConOut, &g_cci);
2562 GetConsoleMode(g_hConIn, &g_cmodein);
2563 GetConsoleMode(g_hConOut, &g_cmodeout);
2564
2565#ifdef FEAT_TITLE
2566 SaveConsoleTitleAndIcon();
2567 /*
2568 * Set both the small and big icons of the console window to Vim's icon.
2569 * Note that Vim presently only has one size of icon (32x32), but it
2570 * automatically gets scaled down to 16x16 when setting the small icon.
2571 */
2572 if (g_fCanChangeIcon)
2573 SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon);
2574#endif
2575
2576 ui_get_shellsize();
2577
2578#ifdef MCH_WRITE_DUMP
2579 fdDump = fopen("dump", "wt");
2580
2581 if (fdDump)
2582 {
2583 time_t t;
2584
2585 time(&t);
2586 fputs(ctime(&t), fdDump);
2587 fflush(fdDump);
2588 }
2589#endif
2590
2591 g_fWindInitCalled = TRUE;
2592
2593#ifdef FEAT_MOUSE
2594 g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
2595#endif
2596
2597#ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002598 win_clip_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002599#endif
2600
2601 /* This will be NULL on anything but NT 4.0 */
2602 s_pfnGetConsoleKeyboardLayoutName =
2603 (PFNGCKLN) GetProcAddress(GetModuleHandle("kernel32.dll"),
2604 "GetConsoleKeyboardLayoutNameA");
2605}
2606
2607/*
2608 * non-GUI version of mch_exit().
2609 * Shut down and exit with status `r'
2610 * Careful: mch_exit() may be called before mch_init()!
2611 */
2612 void
2613mch_exit(int r)
2614{
2615 stoptermcap();
2616
2617 if (g_fWindInitCalled)
2618 settmode(TMODE_COOK);
2619
2620 ml_close_all(TRUE); /* remove all memfiles */
2621
2622 if (g_fWindInitCalled)
2623 {
2624#ifdef FEAT_TITLE
2625 mch_restore_title(3);
2626 /*
2627 * Restore both the small and big icons of the console window to
2628 * what they were at startup. Don't do this when the window is
2629 * closed, Vim would hang here.
2630 */
2631 if (g_fCanChangeIcon && !g_fForceExit)
2632 SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon);
2633#endif
2634
2635#ifdef MCH_WRITE_DUMP
2636 if (fdDump)
2637 {
2638 time_t t;
2639
2640 time(&t);
2641 fputs(ctime(&t), fdDump);
2642 fclose(fdDump);
2643 }
2644 fdDump = NULL;
2645#endif
2646 }
2647
2648 SetConsoleCursorInfo(g_hConOut, &g_cci);
2649 SetConsoleMode(g_hConIn, g_cmodein);
2650 SetConsoleMode(g_hConOut, g_cmodeout);
2651
2652#ifdef DYNAMIC_GETTEXT
2653 dyn_libintl_end();
2654#endif
2655
2656 exit(r);
2657}
2658#endif /* !FEAT_GUI_W32 */
2659
Bram Moolenaar071d4272004-06-13 20:20:40 +00002660/*
2661 * Do we have an interactive window?
2662 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00002663/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00002664 int
2665mch_check_win(
2666 int argc,
2667 char **argv)
2668{
2669 get_exe_name();
2670
2671#ifdef FEAT_GUI_W32
2672 return OK; /* GUI always has a tty */
2673#else
2674 if (isatty(1))
2675 return OK;
2676 return FAIL;
2677#endif
2678}
2679
2680
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002681#ifdef FEAT_MBYTE
2682/*
2683 * fname_casew(): Wide version of fname_case(). Set the case of the file name,
2684 * if it already exists. When "len" is > 0, also expand short to long
2685 * filenames.
2686 * Return FAIL if wide functions are not available, OK otherwise.
2687 * NOTE: much of this is identical to fname_case(), keep in sync!
2688 */
2689 static int
2690fname_casew(
2691 WCHAR *name,
2692 int len)
2693{
2694 WCHAR szTrueName[_MAX_PATH + 2];
2695 WCHAR szTrueNameTemp[_MAX_PATH + 2];
2696 WCHAR *ptrue, *ptruePrev;
2697 WCHAR *porig, *porigPrev;
2698 int flen;
2699 WIN32_FIND_DATAW fb;
Bram Moolenaar73c61632013-12-07 14:48:10 +01002700 HANDLE hFind = INVALID_HANDLE_VALUE;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002701 int c;
2702 int slen;
2703
2704 flen = (int)wcslen(name);
2705 if (flen > _MAX_PATH)
2706 return OK;
2707
2708 /* slash_adjust(name) not needed, already adjusted by fname_case(). */
2709
2710 /* Build the new name in szTrueName[] one component at a time. */
2711 porig = name;
2712 ptrue = szTrueName;
2713
2714 if (iswalpha(porig[0]) && porig[1] == L':')
2715 {
2716 /* copy leading drive letter */
2717 *ptrue++ = *porig++;
2718 *ptrue++ = *porig++;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002719 }
Bram Moolenaar73c61632013-12-07 14:48:10 +01002720 *ptrue = NUL; /* in case nothing follows */
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002721
2722 while (*porig != NUL)
2723 {
2724 /* copy \ characters */
2725 while (*porig == psepc)
2726 *ptrue++ = *porig++;
2727
2728 ptruePrev = ptrue;
2729 porigPrev = porig;
2730 while (*porig != NUL && *porig != psepc)
2731 {
2732 *ptrue++ = *porig++;
2733 }
2734 *ptrue = NUL;
2735
2736 /* To avoid a slow failure append "\*" when searching a directory,
2737 * server or network share. */
2738 wcscpy(szTrueNameTemp, szTrueName);
2739 slen = (int)wcslen(szTrueNameTemp);
2740 if (*porig == psepc && slen + 2 < _MAX_PATH)
2741 wcscpy(szTrueNameTemp + slen, L"\\*");
2742
2743 /* Skip "", "." and "..". */
2744 if (ptrue > ptruePrev
2745 && (ptruePrev[0] != L'.'
2746 || (ptruePrev[1] != NUL
2747 && (ptruePrev[1] != L'.' || ptruePrev[2] != NUL)))
2748 && (hFind = FindFirstFileW(szTrueNameTemp, &fb))
2749 != INVALID_HANDLE_VALUE)
2750 {
2751 c = *porig;
2752 *porig = NUL;
2753
2754 /* Only use the match when it's the same name (ignoring case) or
2755 * expansion is allowed and there is a match with the short name
2756 * and there is enough room. */
2757 if (_wcsicoll(porigPrev, fb.cFileName) == 0
2758 || (len > 0
2759 && (_wcsicoll(porigPrev, fb.cAlternateFileName) == 0
2760 && (int)(ptruePrev - szTrueName)
2761 + (int)wcslen(fb.cFileName) < len)))
2762 {
2763 wcscpy(ptruePrev, fb.cFileName);
2764
2765 /* Look for exact match and prefer it if found. Must be a
2766 * long name, otherwise there would be only one match. */
2767 while (FindNextFileW(hFind, &fb))
2768 {
2769 if (*fb.cAlternateFileName != NUL
2770 && (wcscoll(porigPrev, fb.cFileName) == 0
2771 || (len > 0
2772 && (_wcsicoll(porigPrev,
2773 fb.cAlternateFileName) == 0
2774 && (int)(ptruePrev - szTrueName)
2775 + (int)wcslen(fb.cFileName) < len))))
2776 {
2777 wcscpy(ptruePrev, fb.cFileName);
2778 break;
2779 }
2780 }
2781 }
2782 FindClose(hFind);
2783 *porig = c;
2784 ptrue = ptruePrev + wcslen(ptruePrev);
2785 }
2786 else if (hFind == INVALID_HANDLE_VALUE
2787 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2788 return FAIL;
2789 }
2790
2791 wcscpy(name, szTrueName);
2792 return OK;
2793}
2794#endif
2795
Bram Moolenaar071d4272004-06-13 20:20:40 +00002796/*
2797 * fname_case(): Set the case of the file name, if it already exists.
2798 * When "len" is > 0, also expand short to long filenames.
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002799 * NOTE: much of this is identical to fname_casew(), keep in sync!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800 */
2801 void
2802fname_case(
2803 char_u *name,
2804 int len)
2805{
2806 char szTrueName[_MAX_PATH + 2];
Bram Moolenaar464c9252010-10-13 20:37:41 +02002807 char szTrueNameTemp[_MAX_PATH + 2];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002808 char *ptrue, *ptruePrev;
2809 char *porig, *porigPrev;
2810 int flen;
2811 WIN32_FIND_DATA fb;
2812 HANDLE hFind;
2813 int c;
Bram Moolenaar464c9252010-10-13 20:37:41 +02002814 int slen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002815
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +00002816 flen = (int)STRLEN(name);
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002817 if (flen == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002818 return;
2819
2820 slash_adjust(name);
2821
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002822#ifdef FEAT_MBYTE
2823 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2824 {
2825 WCHAR *p = enc_to_utf16(name, NULL);
2826
2827 if (p != NULL)
2828 {
2829 char_u *q;
Bram Moolenaar21d89b62014-10-07 10:38:40 +02002830 WCHAR buf[_MAX_PATH + 1];
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002831
Bram Moolenaar21d89b62014-10-07 10:38:40 +02002832 wcsncpy(buf, p, _MAX_PATH);
2833 buf[_MAX_PATH] = L'\0';
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002834 vim_free(p);
2835
2836 if (fname_casew(buf, (len > 0) ? _MAX_PATH : 0) == OK)
2837 {
2838 q = utf16_to_enc(buf, NULL);
2839 if (q != NULL)
2840 {
2841 vim_strncpy(name, q, (len > 0) ? len - 1 : flen);
2842 vim_free(q);
2843 return;
2844 }
2845 }
2846 }
2847 /* Retry with non-wide function (for Windows 98). */
2848 }
2849#endif
2850
2851 /* If 'enc' is utf-8, flen can be larger than _MAX_PATH.
2852 * So we should check this after calling wide function. */
2853 if (flen > _MAX_PATH)
2854 return;
2855
Bram Moolenaar071d4272004-06-13 20:20:40 +00002856 /* Build the new name in szTrueName[] one component at a time. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002857 porig = (char *)name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002858 ptrue = szTrueName;
2859
2860 if (isalpha(porig[0]) && porig[1] == ':')
2861 {
2862 /* copy leading drive letter */
2863 *ptrue++ = *porig++;
2864 *ptrue++ = *porig++;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002865 }
Bram Moolenaar73c61632013-12-07 14:48:10 +01002866 *ptrue = NUL; /* in case nothing follows */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002867
2868 while (*porig != NUL)
2869 {
2870 /* copy \ characters */
2871 while (*porig == psepc)
2872 *ptrue++ = *porig++;
2873
2874 ptruePrev = ptrue;
2875 porigPrev = porig;
2876 while (*porig != NUL && *porig != psepc)
2877 {
2878#ifdef FEAT_MBYTE
2879 int l;
2880
2881 if (enc_dbcs)
2882 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002883 l = (*mb_ptr2len)((char_u *)porig);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002884 while (--l >= 0)
2885 *ptrue++ = *porig++;
2886 }
2887 else
2888#endif
2889 *ptrue++ = *porig++;
2890 }
2891 *ptrue = NUL;
2892
Bram Moolenaar464c9252010-10-13 20:37:41 +02002893 /* To avoid a slow failure append "\*" when searching a directory,
2894 * server or network share. */
2895 STRCPY(szTrueNameTemp, szTrueName);
Bram Moolenaar6b5ef062010-10-27 12:18:00 +02002896 slen = (int)strlen(szTrueNameTemp);
Bram Moolenaar464c9252010-10-13 20:37:41 +02002897 if (*porig == psepc && slen + 2 < _MAX_PATH)
2898 STRCPY(szTrueNameTemp + slen, "\\*");
2899
Bram Moolenaar071d4272004-06-13 20:20:40 +00002900 /* Skip "", "." and "..". */
2901 if (ptrue > ptruePrev
2902 && (ptruePrev[0] != '.'
2903 || (ptruePrev[1] != NUL
2904 && (ptruePrev[1] != '.' || ptruePrev[2] != NUL)))
Bram Moolenaar464c9252010-10-13 20:37:41 +02002905 && (hFind = FindFirstFile(szTrueNameTemp, &fb))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002906 != INVALID_HANDLE_VALUE)
2907 {
2908 c = *porig;
2909 *porig = NUL;
2910
2911 /* Only use the match when it's the same name (ignoring case) or
2912 * expansion is allowed and there is a match with the short name
2913 * and there is enough room. */
2914 if (_stricoll(porigPrev, fb.cFileName) == 0
2915 || (len > 0
2916 && (_stricoll(porigPrev, fb.cAlternateFileName) == 0
2917 && (int)(ptruePrev - szTrueName)
2918 + (int)strlen(fb.cFileName) < len)))
2919 {
2920 STRCPY(ptruePrev, fb.cFileName);
2921
2922 /* Look for exact match and prefer it if found. Must be a
2923 * long name, otherwise there would be only one match. */
2924 while (FindNextFile(hFind, &fb))
2925 {
2926 if (*fb.cAlternateFileName != NUL
2927 && (strcoll(porigPrev, fb.cFileName) == 0
2928 || (len > 0
2929 && (_stricoll(porigPrev,
2930 fb.cAlternateFileName) == 0
2931 && (int)(ptruePrev - szTrueName)
2932 + (int)strlen(fb.cFileName) < len))))
2933 {
2934 STRCPY(ptruePrev, fb.cFileName);
2935 break;
2936 }
2937 }
2938 }
2939 FindClose(hFind);
2940 *porig = c;
2941 ptrue = ptruePrev + strlen(ptruePrev);
2942 }
2943 }
2944
2945 STRCPY(name, szTrueName);
2946}
2947
2948
2949/*
2950 * Insert user name in s[len].
2951 */
2952 int
2953mch_get_user_name(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002954 char_u *s,
2955 int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002956{
Bram Moolenaar41a09032007-10-01 18:34:34 +00002957 char szUserName[256 + 1]; /* UNLEN is 256 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002958 DWORD cch = sizeof szUserName;
2959
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01002960#ifdef FEAT_MBYTE
2961 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2962 {
2963 WCHAR wszUserName[256 + 1]; /* UNLEN is 256 */
2964 DWORD wcch = sizeof(wszUserName) / sizeof(WCHAR);
2965
2966 if (GetUserNameW(wszUserName, &wcch))
2967 {
2968 char_u *p = utf16_to_enc(wszUserName, NULL);
2969
2970 if (p != NULL)
2971 {
2972 vim_strncpy(s, p, len - 1);
2973 vim_free(p);
2974 return OK;
2975 }
2976 }
Bram Moolenaarcd981f22014-02-11 17:06:00 +01002977 else if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
2978 return FAIL;
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01002979 /* Retry with non-wide function (for Windows 98). */
2980 }
2981#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002982 if (GetUserName(szUserName, &cch))
2983 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002984 vim_strncpy(s, (char_u *)szUserName, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002985 return OK;
2986 }
2987 s[0] = NUL;
2988 return FAIL;
2989}
2990
2991
2992/*
2993 * Insert host name in s[len].
2994 */
2995 void
2996mch_get_host_name(
2997 char_u *s,
2998 int len)
2999{
3000 DWORD cch = len;
3001
Bram Moolenaar2cc87382013-12-11 18:21:45 +01003002#ifdef FEAT_MBYTE
3003 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3004 {
3005 WCHAR wszHostName[256 + 1];
3006 DWORD wcch = sizeof(wszHostName) / sizeof(WCHAR);
3007
3008 if (GetComputerNameW(wszHostName, &wcch))
3009 {
3010 char_u *p = utf16_to_enc(wszHostName, NULL);
3011
3012 if (p != NULL)
3013 {
3014 vim_strncpy(s, p, len - 1);
3015 vim_free(p);
3016 return;
3017 }
3018 }
Bram Moolenaarcd981f22014-02-11 17:06:00 +01003019 else if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3020 return;
Bram Moolenaar2cc87382013-12-11 18:21:45 +01003021 /* Retry with non-wide function (for Windows 98). */
3022 }
3023#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003024 if (!GetComputerName((LPSTR)s, &cch))
3025 vim_strncpy(s, (char_u *)"PC (Win32 Vim)", len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003026}
3027
3028
3029/*
3030 * return process ID
3031 */
3032 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003033mch_get_pid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003034{
3035 return (long)GetCurrentProcessId();
3036}
3037
3038
3039/*
3040 * Get name of current directory into buffer 'buf' of length 'len' bytes.
3041 * Return OK for success, FAIL for failure.
3042 */
3043 int
3044mch_dirname(
3045 char_u *buf,
3046 int len)
3047{
3048 /*
3049 * Originally this was:
3050 * return (getcwd(buf, len) != NULL ? OK : FAIL);
3051 * But the Win32s known bug list says that getcwd() doesn't work
3052 * so use the Win32 system call instead. <Negri>
3053 */
3054#ifdef FEAT_MBYTE
3055 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3056 {
3057 WCHAR wbuf[_MAX_PATH + 1];
3058
3059 if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0)
3060 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00003061 char_u *p = utf16_to_enc(wbuf, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003062
3063 if (p != NULL)
3064 {
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00003065 vim_strncpy(buf, p, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066 vim_free(p);
3067 return OK;
3068 }
3069 }
Bram Moolenaarcd981f22014-02-11 17:06:00 +01003070 else if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3071 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003072 /* Retry with non-wide function (for Windows 98). */
3073 }
3074#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003075 return (GetCurrentDirectory(len, (LPSTR)buf) != 0 ? OK : FAIL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003076}
3077
3078/*
Bram Moolenaarffa22202013-11-21 12:34:11 +01003079 * Get file permissions for "name".
3080 * Return mode_t or -1 for error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003081 */
3082 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003083mch_getperm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003084{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003085 struct stat st;
Bram Moolenaarffa22202013-11-21 12:34:11 +01003086 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003087
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003088 n = mch_stat((char *)name, &st);
Bram Moolenaar78cf3f02014-01-10 18:16:07 +01003089 return n == 0 ? (long)(unsigned short)st.st_mode : -1L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003090}
3091
3092
3093/*
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003094 * Set file permission for "name" to "perm".
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003095 *
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003096 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003097 */
3098 int
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003099mch_setperm(char_u *name, long perm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003100{
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003101 long n = -1;
3102
Bram Moolenaar071d4272004-06-13 20:20:40 +00003103#ifdef FEAT_MBYTE
3104 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3105 {
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003106 WCHAR *p = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003107
3108 if (p != NULL)
3109 {
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003110 n = _wchmod(p, perm);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003111 vim_free(p);
Bram Moolenaarcd981f22014-02-11 17:06:00 +01003112 if (n == -1 && g_PlatformId == VER_PLATFORM_WIN32_NT)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003113 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003114 /* Retry with non-wide function (for Windows 98). */
3115 }
3116 }
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003117 if (n == -1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003118#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003119 n = _chmod((const char *)name, perm);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003120 if (n == -1)
3121 return FAIL;
3122
3123 win32_set_archive(name);
3124
3125 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003126}
3127
3128/*
3129 * Set hidden flag for "name".
3130 */
3131 void
3132mch_hide(char_u *name)
3133{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003134 int attrs = win32_getattrs(name);
3135 if (attrs == -1)
3136 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003137
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003138 attrs |= FILE_ATTRIBUTE_HIDDEN;
3139 win32_setattrs(name, attrs);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003140}
3141
3142/*
Bram Moolenaar8a52ba72015-11-02 14:45:56 +01003143 * Return TRUE if file "name" exists and is hidden.
3144 */
3145 int
3146mch_ishidden(char_u *name)
3147{
3148 int f = win32_getattrs(name);
3149
3150 if (f == -1)
3151 return FALSE; /* file does not exist at all */
3152
3153 return (f & FILE_ATTRIBUTE_HIDDEN) != 0;
3154}
3155
3156/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003157 * return TRUE if "name" is a directory
3158 * return FALSE if "name" is not a directory or upon error
3159 */
3160 int
3161mch_isdir(char_u *name)
3162{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003163 int f = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003164
3165 if (f == -1)
3166 return FALSE; /* file does not exist at all */
3167
3168 return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
3169}
3170
3171/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003172 * return TRUE if "name" is a directory, NOT a symlink to a directory
3173 * return FALSE if "name" is not a directory
3174 * return FALSE for error
3175 */
3176 int
3177mch_isrealdir(char_u *name)
3178{
3179 return mch_isdir(name) && !mch_is_symbolic_link(name);
3180}
3181
3182/*
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003183 * Create directory "name".
3184 * Return 0 on success, -1 on error.
3185 */
3186 int
3187mch_mkdir(char_u *name)
3188{
3189#ifdef FEAT_MBYTE
3190 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3191 {
3192 WCHAR *p;
3193 int retval;
3194
3195 p = enc_to_utf16(name, NULL);
3196 if (p == NULL)
3197 return -1;
3198 retval = _wmkdir(p);
3199 vim_free(p);
3200 return retval;
3201 }
3202#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003203 return _mkdir((const char *)name);
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003204}
3205
3206/*
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003207 * Delete directory "name".
3208 * Return 0 on success, -1 on error.
3209 */
3210 int
3211mch_rmdir(char_u *name)
3212{
3213#ifdef FEAT_MBYTE
3214 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3215 {
3216 WCHAR *p;
3217 int retval;
3218
3219 p = enc_to_utf16(name, NULL);
3220 if (p == NULL)
3221 return -1;
3222 retval = _wrmdir(p);
3223 vim_free(p);
3224 return retval;
3225 }
3226#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003227 return _rmdir((const char *)name);
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003228}
3229
3230/*
Bram Moolenaar03f48552006-02-28 23:52:23 +00003231 * Return TRUE if file "fname" has more than one link.
3232 */
3233 int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003234mch_is_hard_link(char_u *fname)
Bram Moolenaar03f48552006-02-28 23:52:23 +00003235{
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003236 BY_HANDLE_FILE_INFORMATION info;
3237
3238 return win32_fileinfo(fname, &info) == FILEINFO_OK
3239 && info.nNumberOfLinks > 1;
3240}
3241
3242/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003243 * Return TRUE if "name" is a symbolic link (or a junction).
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003244 */
3245 int
Bram Moolenaar203258c2016-01-17 22:15:16 +01003246mch_is_symbolic_link(char_u *name)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003247{
3248 HANDLE hFind;
3249 int res = FALSE;
3250 WIN32_FIND_DATAA findDataA;
3251 DWORD fileFlags = 0, reparseTag = 0;
3252#ifdef FEAT_MBYTE
3253 WCHAR *wn = NULL;
3254 WIN32_FIND_DATAW findDataW;
3255
3256 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar203258c2016-01-17 22:15:16 +01003257 wn = enc_to_utf16(name, NULL);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003258 if (wn != NULL)
3259 {
3260 hFind = FindFirstFileW(wn, &findDataW);
3261 vim_free(wn);
3262 if (hFind == INVALID_HANDLE_VALUE
3263 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3264 {
3265 /* Retry with non-wide function (for Windows 98). */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003266 hFind = FindFirstFile((LPCSTR)name, &findDataA);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003267 if (hFind != INVALID_HANDLE_VALUE)
3268 {
3269 fileFlags = findDataA.dwFileAttributes;
3270 reparseTag = findDataA.dwReserved0;
3271 }
3272 }
3273 else
3274 {
3275 fileFlags = findDataW.dwFileAttributes;
3276 reparseTag = findDataW.dwReserved0;
3277 }
3278 }
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003279 else
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003280#endif
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003281 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003282 hFind = FindFirstFile((LPCSTR)name, &findDataA);
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003283 if (hFind != INVALID_HANDLE_VALUE)
3284 {
3285 fileFlags = findDataA.dwFileAttributes;
3286 reparseTag = findDataA.dwReserved0;
3287 }
3288 }
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003289
3290 if (hFind != INVALID_HANDLE_VALUE)
3291 FindClose(hFind);
3292
3293 if ((fileFlags & FILE_ATTRIBUTE_REPARSE_POINT)
Bram Moolenaar203258c2016-01-17 22:15:16 +01003294 && (reparseTag == IO_REPARSE_TAG_SYMLINK
3295 || reparseTag == IO_REPARSE_TAG_MOUNT_POINT))
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003296 res = TRUE;
3297
3298 return res;
3299}
3300
3301/*
3302 * Return TRUE if file "fname" has more than one link or if it is a symbolic
3303 * link.
3304 */
3305 int
3306mch_is_linked(char_u *fname)
3307{
3308 if (mch_is_hard_link(fname) || mch_is_symbolic_link(fname))
3309 return TRUE;
3310 return FALSE;
3311}
3312
3313/*
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003314 * Get the by-handle-file-information for "fname".
3315 * Returns FILEINFO_OK when OK.
3316 * returns FILEINFO_ENC_FAIL when enc_to_utf16() failed.
3317 * Returns FILEINFO_READ_FAIL when CreateFile() failed.
3318 * Returns FILEINFO_INFO_FAIL when GetFileInformationByHandle() failed.
3319 */
3320 int
3321win32_fileinfo(char_u *fname, BY_HANDLE_FILE_INFORMATION *info)
3322{
Bram Moolenaar03f48552006-02-28 23:52:23 +00003323 HANDLE hFile;
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003324 int res = FILEINFO_READ_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003325#ifdef FEAT_MBYTE
3326 WCHAR *wn = NULL;
3327
3328 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003329 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00003330 wn = enc_to_utf16(fname, NULL);
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003331 if (wn == NULL)
3332 res = FILEINFO_ENC_FAIL;
3333 }
Bram Moolenaar03f48552006-02-28 23:52:23 +00003334 if (wn != NULL)
3335 {
3336 hFile = CreateFileW(wn, /* file name */
3337 GENERIC_READ, /* access mode */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003338 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003339 NULL, /* security descriptor */
3340 OPEN_EXISTING, /* creation disposition */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003341 FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003342 NULL); /* handle to template file */
3343 if (hFile == INVALID_HANDLE_VALUE
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003344 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
Bram Moolenaar03f48552006-02-28 23:52:23 +00003345 {
3346 /* Retry with non-wide function (for Windows 98). */
3347 vim_free(wn);
3348 wn = NULL;
3349 }
3350 }
3351 if (wn == NULL)
3352#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003353 hFile = CreateFile((LPCSTR)fname, /* file name */
3354 GENERIC_READ, /* access mode */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003355 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003356 NULL, /* security descriptor */
3357 OPEN_EXISTING, /* creation disposition */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003358 FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003359 NULL); /* handle to template file */
3360
3361 if (hFile != INVALID_HANDLE_VALUE)
3362 {
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003363 if (GetFileInformationByHandle(hFile, info) != 0)
3364 res = FILEINFO_OK;
3365 else
3366 res = FILEINFO_INFO_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003367 CloseHandle(hFile);
3368 }
3369
3370#ifdef FEAT_MBYTE
3371 vim_free(wn);
3372#endif
3373 return res;
3374}
3375
3376/*
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003377 * get file attributes for `name'
3378 * -1 : error
3379 * else FILE_ATTRIBUTE_* defined in winnt.h
3380 */
Bram Moolenaarffa22202013-11-21 12:34:11 +01003381 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003382win32_getattrs(char_u *name)
3383{
3384 int attr;
3385#ifdef FEAT_MBYTE
3386 WCHAR *p = NULL;
3387
3388 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3389 p = enc_to_utf16(name, NULL);
3390
3391 if (p != NULL)
3392 {
3393 attr = GetFileAttributesW(p);
3394 if (attr < 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3395 {
3396 /* Retry with non-wide function (for Windows 98). */
3397 vim_free(p);
3398 p = NULL;
3399 }
3400 }
3401 if (p == NULL)
3402#endif
3403 attr = GetFileAttributes((char *)name);
3404#ifdef FEAT_MBYTE
3405 vim_free(p);
3406#endif
3407 return attr;
3408}
3409
3410/*
3411 * set file attributes for `name' to `attrs'
3412 *
3413 * return -1 for failure, 0 otherwise
3414 */
3415 static
3416 int
3417win32_setattrs(char_u *name, int attrs)
3418{
3419 int res;
3420#ifdef FEAT_MBYTE
3421 WCHAR *p = NULL;
3422
3423 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3424 p = enc_to_utf16(name, NULL);
3425
3426 if (p != NULL)
3427 {
3428 res = SetFileAttributesW(p, attrs);
3429 if (res == FALSE
3430 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3431 {
3432 /* Retry with non-wide function (for Windows 98). */
3433 vim_free(p);
3434 p = NULL;
3435 }
3436 }
3437 if (p == NULL)
3438#endif
3439 res = SetFileAttributes((char *)name, attrs);
3440#ifdef FEAT_MBYTE
3441 vim_free(p);
3442#endif
3443 return res ? 0 : -1;
3444}
3445
3446/*
3447 * Set archive flag for "name".
3448 */
3449 static
3450 int
3451win32_set_archive(char_u *name)
3452{
3453 int attrs = win32_getattrs(name);
3454 if (attrs == -1)
3455 return -1;
3456
3457 attrs |= FILE_ATTRIBUTE_ARCHIVE;
3458 return win32_setattrs(name, attrs);
3459}
3460
3461/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003462 * Return TRUE if file or directory "name" is writable (not readonly).
3463 * Strange semantics of Win32: a readonly directory is writable, but you can't
3464 * delete a file. Let's say this means it is writable.
3465 */
3466 int
3467mch_writable(char_u *name)
3468{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003469 int attrs = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003470
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003471 return (attrs != -1 && (!(attrs & FILE_ATTRIBUTE_READONLY)
3472 || (attrs & FILE_ATTRIBUTE_DIRECTORY)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003473}
3474
Bram Moolenaar071d4272004-06-13 20:20:40 +00003475/*
3476 * Return 1 if "name" can be executed, 0 if not.
Bram Moolenaar77b77102015-03-21 22:18:41 +01003477 * If "use_path" is FALSE only check if "name" is executable.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478 * Return -1 if unknown.
3479 */
3480 int
Bram Moolenaar77b77102015-03-21 22:18:41 +01003481mch_can_exe(char_u *name, char_u **path, int use_path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003482{
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003483 char_u buf[_MAX_PATH];
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003484 int len = (int)STRLEN(name);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003485 char_u *p;
3486
3487 if (len >= _MAX_PATH) /* safety check */
3488 return FALSE;
Bram Moolenaar77b77102015-03-21 22:18:41 +01003489 if (!use_path)
3490 {
3491 /* TODO: check if file is really executable. */
3492 return mch_getperm(name) != -1 && !mch_isdir(name);
3493 }
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003494
3495 /* If there already is an extension try using the name directly. Also do
3496 * this with a Unix-shell like 'shell'. */
3497 if (vim_strchr(gettail(name), '.') != NULL
3498 || strstr((char *)gettail(p_sh), "sh") != NULL)
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003499 if (executable_exists((char *)name, path))
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003500 return TRUE;
3501
3502 /*
3503 * Loop over all extensions in $PATHEXT.
3504 */
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00003505 vim_strncpy(buf, name, _MAX_PATH - 1);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003506 p = mch_getenv("PATHEXT");
3507 if (p == NULL)
3508 p = (char_u *)".com;.exe;.bat;.cmd";
3509 while (*p)
3510 {
3511 if (p[0] == '.' && (p[1] == NUL || p[1] == ';'))
3512 {
3513 /* A single "." means no extension is added. */
3514 buf[len] = NUL;
3515 ++p;
3516 if (*p)
3517 ++p;
3518 }
3519 else
3520 copy_option_part(&p, buf + len, _MAX_PATH - len, ";");
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003521 if (executable_exists((char *)buf, path))
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003522 return TRUE;
3523 }
3524 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003525}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003526
3527/*
3528 * Check what "name" is:
3529 * NODE_NORMAL: file or directory (or doesn't exist)
3530 * NODE_WRITABLE: writable device, socket, fifo, etc.
3531 * NODE_OTHER: non-writable things
3532 */
3533 int
3534mch_nodetype(char_u *name)
3535{
3536 HANDLE hFile;
3537 int type;
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003538#ifdef FEAT_MBYTE
3539 WCHAR *wn = NULL;
3540#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003541
Bram Moolenaar043545e2006-10-10 16:44:07 +00003542 /* We can't open a file with a name "\\.\con" or "\\.\prn" and trying to
3543 * read from it later will cause Vim to hang. Thus return NODE_WRITABLE
3544 * here. */
3545 if (STRNCMP(name, "\\\\.\\", 4) == 0)
3546 return NODE_WRITABLE;
3547
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003548#ifdef FEAT_MBYTE
3549 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3550 {
3551 wn = enc_to_utf16(name, NULL);
3552 if (wn != NULL)
3553 {
3554 hFile = CreateFileW(wn, /* file name */
3555 GENERIC_WRITE, /* access mode */
3556 0, /* share mode */
3557 NULL, /* security descriptor */
3558 OPEN_EXISTING, /* creation disposition */
3559 0, /* file attributes */
3560 NULL); /* handle to template file */
3561 if (hFile == INVALID_HANDLE_VALUE
3562 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3563 {
3564 /* Retry with non-wide function (for Windows 98). */
3565 vim_free(wn);
3566 wn = NULL;
3567 }
3568 }
3569 }
3570 if (wn == NULL)
3571#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003572 hFile = CreateFile((LPCSTR)name, /* file name */
3573 GENERIC_WRITE, /* access mode */
3574 0, /* share mode */
3575 NULL, /* security descriptor */
3576 OPEN_EXISTING, /* creation disposition */
3577 0, /* file attributes */
3578 NULL); /* handle to template file */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003579
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003580#ifdef FEAT_MBYTE
3581 vim_free(wn);
3582#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003583 if (hFile == INVALID_HANDLE_VALUE)
3584 return NODE_NORMAL;
3585
3586 type = GetFileType(hFile);
3587 CloseHandle(hFile);
3588 if (type == FILE_TYPE_CHAR)
3589 return NODE_WRITABLE;
3590 if (type == FILE_TYPE_DISK)
3591 return NODE_NORMAL;
3592 return NODE_OTHER;
3593}
3594
3595#ifdef HAVE_ACL
3596struct my_acl
3597{
3598 PSECURITY_DESCRIPTOR pSecurityDescriptor;
3599 PSID pSidOwner;
3600 PSID pSidGroup;
3601 PACL pDacl;
3602 PACL pSacl;
3603};
3604#endif
3605
3606/*
3607 * Return a pointer to the ACL of file "fname" in allocated memory.
3608 * Return NULL if the ACL is not available for whatever reason.
3609 */
3610 vim_acl_T
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003611mch_get_acl(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612{
3613#ifndef HAVE_ACL
3614 return (vim_acl_T)NULL;
3615#else
3616 struct my_acl *p = NULL;
Bram Moolenaar27515922013-06-29 15:36:26 +02003617 DWORD err;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003618
3619 /* This only works on Windows NT and 2000. */
3620 if (g_PlatformId == VER_PLATFORM_WIN32_NT && advapi_lib != NULL)
3621 {
3622 p = (struct my_acl *)alloc_clear((unsigned)sizeof(struct my_acl));
3623 if (p != NULL)
3624 {
Bram Moolenaar27515922013-06-29 15:36:26 +02003625# ifdef FEAT_MBYTE
3626 WCHAR *wn = NULL;
3627
3628 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3629 wn = enc_to_utf16(fname, NULL);
3630 if (wn != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003631 {
Bram Moolenaar27515922013-06-29 15:36:26 +02003632 /* Try to retrieve the entire security descriptor. */
3633 err = pGetNamedSecurityInfoW(
3634 wn, // Abstract filename
3635 SE_FILE_OBJECT, // File Object
3636 OWNER_SECURITY_INFORMATION |
3637 GROUP_SECURITY_INFORMATION |
3638 DACL_SECURITY_INFORMATION |
3639 SACL_SECURITY_INFORMATION,
3640 &p->pSidOwner, // Ownership information.
3641 &p->pSidGroup, // Group membership.
3642 &p->pDacl, // Discretionary information.
3643 &p->pSacl, // For auditing purposes.
3644 &p->pSecurityDescriptor);
3645 if (err == ERROR_ACCESS_DENIED ||
3646 err == ERROR_PRIVILEGE_NOT_HELD)
3647 {
3648 /* Retrieve only DACL. */
3649 (void)pGetNamedSecurityInfoW(
3650 wn,
3651 SE_FILE_OBJECT,
3652 DACL_SECURITY_INFORMATION,
3653 NULL,
3654 NULL,
3655 &p->pDacl,
3656 NULL,
3657 &p->pSecurityDescriptor);
3658 }
3659 if (p->pSecurityDescriptor == NULL)
3660 {
3661 mch_free_acl((vim_acl_T)p);
3662 p = NULL;
3663 }
3664 vim_free(wn);
3665 }
3666 else
3667# endif
3668 {
3669 /* Try to retrieve the entire security descriptor. */
3670 err = pGetNamedSecurityInfo(
3671 (LPSTR)fname, // Abstract filename
3672 SE_FILE_OBJECT, // File Object
3673 OWNER_SECURITY_INFORMATION |
3674 GROUP_SECURITY_INFORMATION |
3675 DACL_SECURITY_INFORMATION |
3676 SACL_SECURITY_INFORMATION,
3677 &p->pSidOwner, // Ownership information.
3678 &p->pSidGroup, // Group membership.
3679 &p->pDacl, // Discretionary information.
3680 &p->pSacl, // For auditing purposes.
3681 &p->pSecurityDescriptor);
3682 if (err == ERROR_ACCESS_DENIED ||
3683 err == ERROR_PRIVILEGE_NOT_HELD)
3684 {
3685 /* Retrieve only DACL. */
3686 (void)pGetNamedSecurityInfo(
3687 (LPSTR)fname,
3688 SE_FILE_OBJECT,
3689 DACL_SECURITY_INFORMATION,
3690 NULL,
3691 NULL,
3692 &p->pDacl,
3693 NULL,
3694 &p->pSecurityDescriptor);
3695 }
3696 if (p->pSecurityDescriptor == NULL)
3697 {
3698 mch_free_acl((vim_acl_T)p);
3699 p = NULL;
3700 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003701 }
3702 }
3703 }
3704
3705 return (vim_acl_T)p;
3706#endif
3707}
3708
Bram Moolenaar27515922013-06-29 15:36:26 +02003709#ifdef HAVE_ACL
3710/*
3711 * Check if "acl" contains inherited ACE.
3712 */
3713 static BOOL
3714is_acl_inherited(PACL acl)
3715{
3716 DWORD i;
3717 ACL_SIZE_INFORMATION acl_info;
3718 PACCESS_ALLOWED_ACE ace;
3719
3720 acl_info.AceCount = 0;
3721 GetAclInformation(acl, &acl_info, sizeof(acl_info), AclSizeInformation);
3722 for (i = 0; i < acl_info.AceCount; i++)
3723 {
3724 GetAce(acl, i, (LPVOID *)&ace);
3725 if (ace->Header.AceFlags & INHERITED_ACE)
3726 return TRUE;
3727 }
3728 return FALSE;
3729}
3730#endif
3731
Bram Moolenaar071d4272004-06-13 20:20:40 +00003732/*
3733 * Set the ACL of file "fname" to "acl" (unless it's NULL).
3734 * Errors are ignored.
3735 * This must only be called with "acl" equal to what mch_get_acl() returned.
3736 */
3737 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003738mch_set_acl(char_u *fname, vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739{
3740#ifdef HAVE_ACL
3741 struct my_acl *p = (struct my_acl *)acl;
Bram Moolenaar27515922013-06-29 15:36:26 +02003742 SECURITY_INFORMATION sec_info = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003743
3744 if (p != NULL && advapi_lib != NULL)
Bram Moolenaar27515922013-06-29 15:36:26 +02003745 {
3746# ifdef FEAT_MBYTE
3747 WCHAR *wn = NULL;
3748# endif
3749
3750 /* Set security flags */
3751 if (p->pSidOwner)
3752 sec_info |= OWNER_SECURITY_INFORMATION;
3753 if (p->pSidGroup)
3754 sec_info |= GROUP_SECURITY_INFORMATION;
3755 if (p->pDacl)
3756 {
3757 sec_info |= DACL_SECURITY_INFORMATION;
3758 /* Do not inherit its parent's DACL.
3759 * If the DACL is inherited, Cygwin permissions would be changed.
3760 */
3761 if (!is_acl_inherited(p->pDacl))
3762 sec_info |= PROTECTED_DACL_SECURITY_INFORMATION;
3763 }
3764 if (p->pSacl)
3765 sec_info |= SACL_SECURITY_INFORMATION;
3766
3767# ifdef FEAT_MBYTE
3768 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3769 wn = enc_to_utf16(fname, NULL);
3770 if (wn != NULL)
3771 {
3772 (void)pSetNamedSecurityInfoW(
3773 wn, // Abstract filename
3774 SE_FILE_OBJECT, // File Object
3775 sec_info,
3776 p->pSidOwner, // Ownership information.
3777 p->pSidGroup, // Group membership.
3778 p->pDacl, // Discretionary information.
3779 p->pSacl // For auditing purposes.
3780 );
3781 vim_free(wn);
3782 }
3783 else
3784# endif
3785 {
3786 (void)pSetNamedSecurityInfo(
3787 (LPSTR)fname, // Abstract filename
3788 SE_FILE_OBJECT, // File Object
3789 sec_info,
3790 p->pSidOwner, // Ownership information.
3791 p->pSidGroup, // Group membership.
3792 p->pDacl, // Discretionary information.
3793 p->pSacl // For auditing purposes.
3794 );
3795 }
3796 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003797#endif
3798}
3799
3800 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003801mch_free_acl(vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003802{
3803#ifdef HAVE_ACL
3804 struct my_acl *p = (struct my_acl *)acl;
3805
3806 if (p != NULL)
3807 {
3808 LocalFree(p->pSecurityDescriptor); // Free the memory just in case
3809 vim_free(p);
3810 }
3811#endif
3812}
3813
3814#ifndef FEAT_GUI_W32
3815
3816/*
3817 * handler for ctrl-break, ctrl-c interrupts, and fatal events.
3818 */
3819 static BOOL WINAPI
3820handler_routine(
3821 DWORD dwCtrlType)
3822{
3823 switch (dwCtrlType)
3824 {
3825 case CTRL_C_EVENT:
3826 if (ctrl_c_interrupts)
3827 g_fCtrlCPressed = TRUE;
3828 return TRUE;
3829
3830 case CTRL_BREAK_EVENT:
3831 g_fCBrkPressed = TRUE;
3832 return TRUE;
3833
3834 /* fatal events: shut down gracefully */
3835 case CTRL_CLOSE_EVENT:
3836 case CTRL_LOGOFF_EVENT:
3837 case CTRL_SHUTDOWN_EVENT:
3838 windgoto((int)Rows - 1, 0);
3839 g_fForceExit = TRUE;
3840
Bram Moolenaar0fde2902008-03-16 13:54:13 +00003841 vim_snprintf((char *)IObuff, IOSIZE, _("Vim: Caught %s event\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003842 (dwCtrlType == CTRL_CLOSE_EVENT
3843 ? _("close")
3844 : dwCtrlType == CTRL_LOGOFF_EVENT
3845 ? _("logoff")
3846 : _("shutdown")));
3847#ifdef DEBUG
3848 OutputDebugString(IObuff);
3849#endif
3850
3851 preserve_exit(); /* output IObuff, preserve files and exit */
3852
3853 return TRUE; /* not reached */
3854
3855 default:
3856 return FALSE;
3857 }
3858}
3859
3860
3861/*
3862 * set the tty in (raw) ? "raw" : "cooked" mode
3863 */
3864 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003865mch_settmode(int tmode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003866{
3867 DWORD cmodein;
3868 DWORD cmodeout;
3869 BOOL bEnableHandler;
3870
3871 GetConsoleMode(g_hConIn, &cmodein);
3872 GetConsoleMode(g_hConOut, &cmodeout);
3873 if (tmode == TMODE_RAW)
3874 {
3875 cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3876 ENABLE_ECHO_INPUT);
3877#ifdef FEAT_MOUSE
3878 if (g_fMouseActive)
3879 cmodein |= ENABLE_MOUSE_INPUT;
3880#endif
3881 cmodeout &= ~(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3882 bEnableHandler = TRUE;
3883 }
3884 else /* cooked */
3885 {
3886 cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3887 ENABLE_ECHO_INPUT);
3888 cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3889 bEnableHandler = FALSE;
3890 }
3891 SetConsoleMode(g_hConIn, cmodein);
3892 SetConsoleMode(g_hConOut, cmodeout);
3893 SetConsoleCtrlHandler(handler_routine, bEnableHandler);
3894
3895#ifdef MCH_WRITE_DUMP
3896 if (fdDump)
3897 {
3898 fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n",
3899 tmode == TMODE_RAW ? "raw" :
3900 tmode == TMODE_COOK ? "cooked" : "normal",
3901 cmodein, cmodeout);
3902 fflush(fdDump);
3903 }
3904#endif
3905}
3906
3907
3908/*
3909 * Get the size of the current window in `Rows' and `Columns'
3910 * Return OK when size could be determined, FAIL otherwise.
3911 */
3912 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003913mch_get_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914{
3915 CONSOLE_SCREEN_BUFFER_INFO csbi;
3916
3917 if (!g_fTermcapMode && g_cbTermcap.IsValid)
3918 {
3919 /*
3920 * For some reason, we are trying to get the screen dimensions
3921 * even though we are not in termcap mode. The 'Rows' and 'Columns'
3922 * variables are really intended to mean the size of Vim screen
3923 * while in termcap mode.
3924 */
3925 Rows = g_cbTermcap.Info.dwSize.Y;
3926 Columns = g_cbTermcap.Info.dwSize.X;
3927 }
3928 else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3929 {
3930 Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3931 Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3932 }
3933 else
3934 {
3935 Rows = 25;
3936 Columns = 80;
3937 }
3938 return OK;
3939}
3940
3941/*
3942 * Set a console window to `xSize' * `ySize'
3943 */
3944 static void
3945ResizeConBufAndWindow(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003946 HANDLE hConsole,
3947 int xSize,
3948 int ySize)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003949{
3950 CONSOLE_SCREEN_BUFFER_INFO csbi; /* hold current console buffer info */
3951 SMALL_RECT srWindowRect; /* hold the new console size */
3952 COORD coordScreen;
3953
3954#ifdef MCH_WRITE_DUMP
3955 if (fdDump)
3956 {
3957 fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
3958 fflush(fdDump);
3959 }
3960#endif
3961
3962 /* get the largest size we can size the console window to */
3963 coordScreen = GetLargestConsoleWindowSize(hConsole);
3964
3965 /* define the new console window size and scroll position */
3966 srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
3967 srWindowRect.Right = (SHORT) (min(xSize, coordScreen.X) - 1);
3968 srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
3969
3970 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3971 {
3972 int sx, sy;
3973
3974 sx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3975 sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3976 if (sy < ySize || sx < xSize)
3977 {
3978 /*
3979 * Increasing number of lines/columns, do buffer first.
3980 * Use the maximal size in x and y direction.
3981 */
3982 if (sy < ySize)
3983 coordScreen.Y = ySize;
3984 else
3985 coordScreen.Y = sy;
3986 if (sx < xSize)
3987 coordScreen.X = xSize;
3988 else
3989 coordScreen.X = sx;
3990 SetConsoleScreenBufferSize(hConsole, coordScreen);
3991 }
3992 }
3993
3994 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &srWindowRect))
3995 {
3996#ifdef MCH_WRITE_DUMP
3997 if (fdDump)
3998 {
3999 fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
4000 GetLastError());
4001 fflush(fdDump);
4002 }
4003#endif
4004 }
4005
4006 /* define the new console buffer size */
4007 coordScreen.X = xSize;
4008 coordScreen.Y = ySize;
4009
4010 if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
4011 {
4012#ifdef MCH_WRITE_DUMP
4013 if (fdDump)
4014 {
4015 fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
4016 GetLastError());
4017 fflush(fdDump);
4018 }
4019#endif
4020 }
4021}
4022
4023
4024/*
4025 * Set the console window to `Rows' * `Columns'
4026 */
4027 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004028mch_set_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029{
4030 COORD coordScreen;
4031
4032 /* Don't change window size while still starting up */
4033 if (suppress_winsize != 0)
4034 {
4035 suppress_winsize = 2;
4036 return;
4037 }
4038
4039 if (term_console)
4040 {
4041 coordScreen = GetLargestConsoleWindowSize(g_hConOut);
4042
4043 /* Clamp Rows and Columns to reasonable values */
4044 if (Rows > coordScreen.Y)
4045 Rows = coordScreen.Y;
4046 if (Columns > coordScreen.X)
4047 Columns = coordScreen.X;
4048
4049 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
4050 }
4051}
4052
4053/*
4054 * Rows and/or Columns has changed.
4055 */
4056 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004057mch_new_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004058{
4059 set_scroll_region(0, 0, Columns - 1, Rows - 1);
4060}
4061
4062
4063/*
4064 * Called when started up, to set the winsize that was delayed.
4065 */
4066 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004067mch_set_winsize_now(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004068{
4069 if (suppress_winsize == 2)
4070 {
4071 suppress_winsize = 0;
4072 mch_set_shellsize();
4073 shell_resized();
4074 }
4075 suppress_winsize = 0;
4076}
4077#endif /* FEAT_GUI_W32 */
4078
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004079 static BOOL
4080vim_create_process(
Bram Moolenaar36c85b22013-12-12 20:25:44 +01004081 char *cmd,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004082 BOOL inherit_handles,
Bram Moolenaar3f1138e2014-01-05 13:29:26 +01004083 DWORD flags,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004084 STARTUPINFO *si,
4085 PROCESS_INFORMATION *pi)
4086{
4087# ifdef FEAT_MBYTE
4088 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4089 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004090 WCHAR *wcmd = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004091
4092 if (wcmd != NULL)
4093 {
4094 BOOL ret;
4095 ret = CreateProcessW(
4096 NULL, /* Executable name */
4097 wcmd, /* Command to execute */
4098 NULL, /* Process security attributes */
4099 NULL, /* Thread security attributes */
4100 inherit_handles, /* Inherit handles */
4101 flags, /* Creation flags */
4102 NULL, /* Environment */
4103 NULL, /* Current directory */
Bram Moolenaar36c85b22013-12-12 20:25:44 +01004104 (LPSTARTUPINFOW)si, /* Startup information */
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004105 pi); /* Process information */
4106 vim_free(wcmd);
4107 return ret;
4108 }
4109 }
4110#endif
4111 return CreateProcess(
4112 NULL, /* Executable name */
4113 cmd, /* Command to execute */
4114 NULL, /* Process security attributes */
4115 NULL, /* Thread security attributes */
4116 inherit_handles, /* Inherit handles */
4117 flags, /* Creation flags */
4118 NULL, /* Environment */
4119 NULL, /* Current directory */
4120 si, /* Startup information */
4121 pi); /* Process information */
4122}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004123
4124
4125#if defined(FEAT_GUI_W32) || defined(PROTO)
4126
4127/*
4128 * Specialised version of system() for Win32 GUI mode.
4129 * This version proceeds as follows:
4130 * 1. Create a console window for use by the subprocess
4131 * 2. Run the subprocess (it gets the allocated console by default)
4132 * 3. Wait for the subprocess to terminate and get its exit code
4133 * 4. Prompt the user to press a key to close the console window
4134 */
4135 static int
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004136mch_system_classic(char *cmd, int options)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004137{
4138 STARTUPINFO si;
4139 PROCESS_INFORMATION pi;
4140 DWORD ret = 0;
4141 HWND hwnd = GetFocus();
4142
4143 si.cb = sizeof(si);
4144 si.lpReserved = NULL;
4145 si.lpDesktop = NULL;
4146 si.lpTitle = NULL;
4147 si.dwFlags = STARTF_USESHOWWINDOW;
4148 /*
4149 * It's nicer to run a filter command in a minimized window, but in
4150 * Windows 95 this makes the command MUCH slower. We can't do it under
4151 * Win32s either as it stops the synchronous spawn workaround working.
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004152 * Don't activate the window to keep focus on Vim.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004153 */
4154 if ((options & SHELL_DOOUT) && !mch_windows95() && !gui_is_win32s())
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004155 si.wShowWindow = SW_SHOWMINNOACTIVE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004156 else
4157 si.wShowWindow = SW_SHOWNORMAL;
4158 si.cbReserved2 = 0;
4159 si.lpReserved2 = NULL;
4160
Bram Moolenaar942d6b22016-02-07 19:57:16 +01004161 /* There is a strange error on Windows 95 when using "c:\command.com".
Bram Moolenaar071d4272004-06-13 20:20:40 +00004162 * When the "c:\\" is left out it works OK...? */
4163 if (mch_windows95()
4164 && (STRNICMP(cmd, "c:/command.com", 14) == 0
4165 || STRNICMP(cmd, "c:\\command.com", 14) == 0))
4166 cmd += 3;
4167
4168 /* Now, run the command */
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004169 vim_create_process(cmd, FALSE,
4170 CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE, &si, &pi);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004171
4172 /* Wait for the command to terminate before continuing */
4173 if (g_PlatformId != VER_PLATFORM_WIN32s)
4174 {
4175#ifdef FEAT_GUI
4176 int delay = 1;
4177
4178 /* Keep updating the window while waiting for the shell to finish. */
4179 for (;;)
4180 {
4181 MSG msg;
4182
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02004183 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004184 {
4185 TranslateMessage(&msg);
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02004186 pDispatchMessage(&msg);
Bram Moolenaare4195c52012-08-02 12:31:44 +02004187 delay = 1;
4188 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004189 }
4190 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4191 break;
4192
4193 /* We start waiting for a very short time and then increase it, so
4194 * that we respond quickly when the process is quick, and don't
4195 * consume too much overhead when it's slow. */
4196 if (delay < 50)
4197 delay += 10;
4198 }
4199#else
4200 WaitForSingleObject(pi.hProcess, INFINITE);
4201#endif
4202
4203 /* Get the command exit code */
4204 GetExitCodeProcess(pi.hProcess, &ret);
4205 }
4206 else
4207 {
4208 /*
4209 * This ugly code is the only quick way of performing
4210 * a synchronous spawn under Win32s. Yuk.
4211 */
4212 num_windows = 0;
4213 EnumWindows(win32ssynch_cb, 0);
4214 old_num_windows = num_windows;
4215 do
4216 {
4217 Sleep(1000);
4218 num_windows = 0;
4219 EnumWindows(win32ssynch_cb, 0);
4220 } while (num_windows == old_num_windows);
4221 ret = 0;
4222 }
4223
4224 /* Close the handles to the subprocess, so that it goes away */
4225 CloseHandle(pi.hThread);
4226 CloseHandle(pi.hProcess);
4227
4228 /* Try to get input focus back. Doesn't always work though. */
4229 PostMessage(hwnd, WM_SETFOCUS, 0, 0);
4230
4231 return ret;
4232}
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004233
4234/*
4235 * Thread launched by the gui to send the current buffer data to the
4236 * process. This way avoid to hang up vim totally if the children
4237 * process take a long time to process the lines.
4238 */
4239 static DWORD WINAPI
4240sub_process_writer(LPVOID param)
4241{
4242 HANDLE g_hChildStd_IN_Wr = param;
4243 linenr_T lnum = curbuf->b_op_start.lnum;
4244 DWORD len = 0;
4245 DWORD l;
4246 char_u *lp = ml_get(lnum);
4247 char_u *s;
4248 int written = 0;
4249
4250 for (;;)
4251 {
4252 l = (DWORD)STRLEN(lp + written);
4253 if (l == 0)
4254 len = 0;
4255 else if (lp[written] == NL)
4256 {
4257 /* NL -> NUL translation */
4258 WriteFile(g_hChildStd_IN_Wr, "", 1, &len, NULL);
4259 }
4260 else
4261 {
4262 s = vim_strchr(lp + written, NL);
4263 WriteFile(g_hChildStd_IN_Wr, (char *)lp + written,
4264 s == NULL ? l : (DWORD)(s - (lp + written)),
4265 &len, NULL);
4266 }
4267 if (len == (int)l)
4268 {
4269 /* Finished a line, add a NL, unless this line should not have
4270 * one. */
4271 if (lnum != curbuf->b_op_end.lnum
Bram Moolenaar34d72d42015-07-17 14:18:08 +02004272 || (!curbuf->b_p_bin
4273 && curbuf->b_p_fixeol)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004274 || (lnum != curbuf->b_no_eol_lnum
4275 && (lnum != curbuf->b_ml.ml_line_count
4276 || curbuf->b_p_eol)))
4277 {
Bram Moolenaaraf62ff32013-03-19 14:48:29 +01004278 WriteFile(g_hChildStd_IN_Wr, "\n", 1, (LPDWORD)&ignored, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004279 }
4280
4281 ++lnum;
4282 if (lnum > curbuf->b_op_end.lnum)
4283 break;
4284
4285 lp = ml_get(lnum);
4286 written = 0;
4287 }
4288 else if (len > 0)
4289 written += len;
4290 }
4291
4292 /* finished all the lines, close pipe */
4293 CloseHandle(g_hChildStd_IN_Wr);
4294 ExitThread(0);
4295}
4296
4297
4298# define BUFLEN 100 /* length for buffer, stolen from unix version */
4299
4300/*
4301 * This function read from the children's stdout and write the
4302 * data on screen or in the buffer accordingly.
4303 */
4304 static void
4305dump_pipe(int options,
4306 HANDLE g_hChildStd_OUT_Rd,
4307 garray_T *ga,
4308 char_u buffer[],
4309 DWORD *buffer_off)
4310{
4311 DWORD availableBytes = 0;
4312 DWORD i;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004313 int ret;
4314 DWORD len;
4315 DWORD toRead;
4316 int repeatCount;
4317
4318 /* we query the pipe to see if there is any data to read
4319 * to avoid to perform a blocking read */
4320 ret = PeekNamedPipe(g_hChildStd_OUT_Rd, /* pipe to query */
4321 NULL, /* optional buffer */
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02004322 0, /* buffer size */
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004323 NULL, /* number of read bytes */
4324 &availableBytes, /* available bytes total */
4325 NULL); /* byteLeft */
4326
4327 repeatCount = 0;
4328 /* We got real data in the pipe, read it */
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02004329 while (ret != 0 && availableBytes > 0)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004330 {
4331 repeatCount++;
4332 toRead =
4333# ifdef FEAT_MBYTE
4334 (DWORD)(BUFLEN - *buffer_off);
4335# else
4336 (DWORD)BUFLEN;
4337# endif
4338 toRead = availableBytes < toRead ? availableBytes : toRead;
4339 ReadFile(g_hChildStd_OUT_Rd, buffer
4340# ifdef FEAT_MBYTE
4341 + *buffer_off, toRead
4342# else
4343 , toRead
4344# endif
4345 , &len, NULL);
4346
4347 /* If we haven't read anything, there is a problem */
4348 if (len == 0)
4349 break;
4350
4351 availableBytes -= len;
4352
4353 if (options & SHELL_READ)
4354 {
4355 /* Do NUL -> NL translation, append NL separated
4356 * lines to the current buffer. */
4357 for (i = 0; i < len; ++i)
4358 {
4359 if (buffer[i] == NL)
4360 append_ga_line(ga);
4361 else if (buffer[i] == NUL)
4362 ga_append(ga, NL);
4363 else
4364 ga_append(ga, buffer[i]);
4365 }
4366 }
4367# ifdef FEAT_MBYTE
4368 else if (has_mbyte)
4369 {
4370 int l;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004371 int c;
4372 char_u *p;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004373
4374 len += *buffer_off;
4375 buffer[len] = NUL;
4376
4377 /* Check if the last character in buffer[] is
4378 * incomplete, keep these bytes for the next
4379 * round. */
4380 for (p = buffer; p < buffer + len; p += l)
4381 {
4382 l = mb_cptr2len(p);
4383 if (l == 0)
4384 l = 1; /* NUL byte? */
4385 else if (MB_BYTE2LEN(*p) != l)
4386 break;
4387 }
4388 if (p == buffer) /* no complete character */
4389 {
4390 /* avoid getting stuck at an illegal byte */
4391 if (len >= 12)
4392 ++p;
4393 else
4394 {
4395 *buffer_off = len;
4396 return;
4397 }
4398 }
4399 c = *p;
4400 *p = NUL;
4401 msg_puts(buffer);
4402 if (p < buffer + len)
4403 {
4404 *p = c;
4405 *buffer_off = (DWORD)((buffer + len) - p);
4406 mch_memmove(buffer, p, *buffer_off);
4407 return;
4408 }
4409 *buffer_off = 0;
4410 }
4411# endif /* FEAT_MBYTE */
4412 else
4413 {
4414 buffer[len] = NUL;
4415 msg_puts(buffer);
4416 }
4417
4418 windgoto(msg_row, msg_col);
4419 cursor_on();
4420 out_flush();
4421 }
4422}
4423
4424/*
4425 * Version of system to use for windows NT > 5.0 (Win2K), use pipe
4426 * for communication and doesn't open any new window.
4427 */
4428 static int
4429mch_system_piped(char *cmd, int options)
4430{
4431 STARTUPINFO si;
4432 PROCESS_INFORMATION pi;
4433 DWORD ret = 0;
4434
4435 HANDLE g_hChildStd_IN_Rd = NULL;
4436 HANDLE g_hChildStd_IN_Wr = NULL;
4437 HANDLE g_hChildStd_OUT_Rd = NULL;
4438 HANDLE g_hChildStd_OUT_Wr = NULL;
4439
4440 char_u buffer[BUFLEN + 1]; /* reading buffer + size */
4441 DWORD len;
4442
4443 /* buffer used to receive keys */
4444 char_u ta_buf[BUFLEN + 1]; /* TypeAHead */
4445 int ta_len = 0; /* valid bytes in ta_buf[] */
4446
4447 DWORD i;
4448 int c;
4449 int noread_cnt = 0;
4450 garray_T ga;
4451 int delay = 1;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004452 DWORD buffer_off = 0; /* valid bytes in buffer[] */
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004453 char *p = NULL;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004454
4455 SECURITY_ATTRIBUTES saAttr;
4456
4457 /* Set the bInheritHandle flag so pipe handles are inherited. */
4458 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
4459 saAttr.bInheritHandle = TRUE;
4460 saAttr.lpSecurityDescriptor = NULL;
4461
4462 if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)
4463 /* Ensure the read handle to the pipe for STDOUT is not inherited. */
4464 || ! pSetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)
4465 /* Create a pipe for the child process's STDIN. */
4466 || ! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)
4467 /* Ensure the write handle to the pipe for STDIN is not inherited. */
4468 || ! pSetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
4469 {
4470 CloseHandle(g_hChildStd_IN_Rd);
4471 CloseHandle(g_hChildStd_IN_Wr);
4472 CloseHandle(g_hChildStd_OUT_Rd);
4473 CloseHandle(g_hChildStd_OUT_Wr);
4474 MSG_PUTS(_("\nCannot create pipes\n"));
4475 }
4476
4477 si.cb = sizeof(si);
4478 si.lpReserved = NULL;
4479 si.lpDesktop = NULL;
4480 si.lpTitle = NULL;
4481 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
4482
4483 /* set-up our file redirection */
4484 si.hStdError = g_hChildStd_OUT_Wr;
4485 si.hStdOutput = g_hChildStd_OUT_Wr;
4486 si.hStdInput = g_hChildStd_IN_Rd;
4487 si.wShowWindow = SW_HIDE;
4488 si.cbReserved2 = 0;
4489 si.lpReserved2 = NULL;
4490
4491 if (options & SHELL_READ)
4492 ga_init2(&ga, 1, BUFLEN);
4493
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004494 if (cmd != NULL)
4495 {
4496 p = (char *)vim_strsave((char_u *)cmd);
4497 if (p != NULL)
4498 unescape_shellxquote((char_u *)p, p_sxe);
4499 else
4500 p = cmd;
4501 }
4502
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004503 /* Now, run the command.
4504 * About "Inherit handles" being TRUE: this command can be litigious,
4505 * handle inheritance was deactivated for pending temp file, but, if we
4506 * deactivate it, the pipes don't work for some reason. */
4507 vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE, &si, &pi);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004508
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004509 if (p != cmd)
4510 vim_free(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004511
4512 /* Close our unused side of the pipes */
4513 CloseHandle(g_hChildStd_IN_Rd);
4514 CloseHandle(g_hChildStd_OUT_Wr);
4515
4516 if (options & SHELL_WRITE)
4517 {
4518 HANDLE thread =
4519 CreateThread(NULL, /* security attributes */
4520 0, /* default stack size */
4521 sub_process_writer, /* function to be executed */
4522 g_hChildStd_IN_Wr, /* parameter */
4523 0, /* creation flag, start immediately */
4524 NULL); /* we don't care about thread id */
4525 CloseHandle(thread);
4526 g_hChildStd_IN_Wr = NULL;
4527 }
4528
4529 /* Keep updating the window while waiting for the shell to finish. */
4530 for (;;)
4531 {
4532 MSG msg;
4533
Bram Moolenaar175d0702013-12-11 18:36:33 +01004534 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004535 {
4536 TranslateMessage(&msg);
Bram Moolenaar175d0702013-12-11 18:36:33 +01004537 pDispatchMessage(&msg);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004538 }
4539
4540 /* write pipe information in the window */
4541 if ((options & (SHELL_READ|SHELL_WRITE))
4542# ifdef FEAT_GUI
4543 || gui.in_use
4544# endif
4545 )
4546 {
4547 len = 0;
4548 if (!(options & SHELL_EXPAND)
4549 && ((options &
4550 (SHELL_READ|SHELL_WRITE|SHELL_COOKED))
4551 != (SHELL_READ|SHELL_WRITE|SHELL_COOKED)
4552# ifdef FEAT_GUI
4553 || gui.in_use
4554# endif
4555 )
4556 && (ta_len > 0 || noread_cnt > 4))
4557 {
4558 if (ta_len == 0)
4559 {
4560 /* Get extra characters when we don't have any. Reset the
4561 * counter and timer. */
4562 noread_cnt = 0;
4563# if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
4564 gettimeofday(&start_tv, NULL);
4565# endif
4566 len = ui_inchar(ta_buf, BUFLEN, 10L, 0);
4567 }
4568 if (ta_len > 0 || len > 0)
4569 {
4570 /*
4571 * For pipes: Check for CTRL-C: send interrupt signal to
4572 * child. Check for CTRL-D: EOF, close pipe to child.
4573 */
4574 if (len == 1 && cmd != NULL)
4575 {
4576 if (ta_buf[ta_len] == Ctrl_C)
4577 {
4578 /* Learn what exit code is expected, for
4579 * now put 9 as SIGKILL */
4580 TerminateProcess(pi.hProcess, 9);
4581 }
4582 if (ta_buf[ta_len] == Ctrl_D)
4583 {
4584 CloseHandle(g_hChildStd_IN_Wr);
4585 g_hChildStd_IN_Wr = NULL;
4586 }
4587 }
4588
4589 /* replace K_BS by <BS> and K_DEL by <DEL> */
4590 for (i = ta_len; i < ta_len + len; ++i)
4591 {
4592 if (ta_buf[i] == CSI && len - i > 2)
4593 {
4594 c = TERMCAP2KEY(ta_buf[i + 1], ta_buf[i + 2]);
4595 if (c == K_DEL || c == K_KDEL || c == K_BS)
4596 {
4597 mch_memmove(ta_buf + i + 1, ta_buf + i + 3,
4598 (size_t)(len - i - 2));
4599 if (c == K_DEL || c == K_KDEL)
4600 ta_buf[i] = DEL;
4601 else
4602 ta_buf[i] = Ctrl_H;
4603 len -= 2;
4604 }
4605 }
4606 else if (ta_buf[i] == '\r')
4607 ta_buf[i] = '\n';
4608# ifdef FEAT_MBYTE
4609 if (has_mbyte)
4610 i += (*mb_ptr2len_len)(ta_buf + i,
4611 ta_len + len - i) - 1;
4612# endif
4613 }
4614
4615 /*
4616 * For pipes: echo the typed characters. For a pty this
4617 * does not seem to work.
4618 */
4619 for (i = ta_len; i < ta_len + len; ++i)
4620 {
4621 if (ta_buf[i] == '\n' || ta_buf[i] == '\b')
4622 msg_putchar(ta_buf[i]);
4623# ifdef FEAT_MBYTE
4624 else if (has_mbyte)
4625 {
4626 int l = (*mb_ptr2len)(ta_buf + i);
4627
4628 msg_outtrans_len(ta_buf + i, l);
4629 i += l - 1;
4630 }
4631# endif
4632 else
4633 msg_outtrans_len(ta_buf + i, 1);
4634 }
4635 windgoto(msg_row, msg_col);
4636 out_flush();
4637
4638 ta_len += len;
4639
4640 /*
4641 * Write the characters to the child, unless EOF has been
4642 * typed for pipes. Write one character at a time, to
4643 * avoid losing too much typeahead. When writing buffer
4644 * lines, drop the typed characters (only check for
4645 * CTRL-C).
4646 */
4647 if (options & SHELL_WRITE)
4648 ta_len = 0;
4649 else if (g_hChildStd_IN_Wr != NULL)
4650 {
4651 WriteFile(g_hChildStd_IN_Wr, (char*)ta_buf,
4652 1, &len, NULL);
4653 // if we are typing in, we want to keep things reactive
4654 delay = 1;
4655 if (len > 0)
4656 {
4657 ta_len -= len;
4658 mch_memmove(ta_buf, ta_buf + len, ta_len);
4659 }
4660 }
4661 }
4662 }
4663 }
4664
4665 if (ta_len)
4666 ui_inchar_undo(ta_buf, ta_len);
4667
4668 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4669 {
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004670 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004671 break;
4672 }
4673
4674 ++noread_cnt;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004675 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004676
4677 /* We start waiting for a very short time and then increase it, so
4678 * that we respond quickly when the process is quick, and don't
4679 * consume too much overhead when it's slow. */
4680 if (delay < 50)
4681 delay += 10;
4682 }
4683
4684 /* Close the pipe */
4685 CloseHandle(g_hChildStd_OUT_Rd);
4686 if (g_hChildStd_IN_Wr != NULL)
4687 CloseHandle(g_hChildStd_IN_Wr);
4688
4689 WaitForSingleObject(pi.hProcess, INFINITE);
4690
4691 /* Get the command exit code */
4692 GetExitCodeProcess(pi.hProcess, &ret);
4693
4694 if (options & SHELL_READ)
4695 {
4696 if (ga.ga_len > 0)
4697 {
4698 append_ga_line(&ga);
4699 /* remember that the NL was missing */
4700 curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
4701 }
4702 else
4703 curbuf->b_no_eol_lnum = 0;
4704 ga_clear(&ga);
4705 }
4706
4707 /* Close the handles to the subprocess, so that it goes away */
4708 CloseHandle(pi.hThread);
4709 CloseHandle(pi.hProcess);
4710
4711 return ret;
4712}
4713
4714 static int
4715mch_system(char *cmd, int options)
4716{
4717 /* if we can pipe and the shelltemp option is off */
4718 if (allowPiping && !p_stmp)
4719 return mch_system_piped(cmd, options);
4720 else
4721 return mch_system_classic(cmd, options);
4722}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004723#else
4724
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004725# ifdef FEAT_MBYTE
4726 static int
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01004727mch_system(char *cmd, int options)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004728{
4729 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4730 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004731 WCHAR *wcmd = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004732 if (wcmd != NULL)
4733 {
4734 int ret = _wsystem(wcmd);
4735 vim_free(wcmd);
4736 return ret;
4737 }
4738 }
4739 return system(cmd);
4740}
4741# else
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01004742# define mch_system(c, o) system(c)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004743# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004744
4745#endif
4746
4747/*
4748 * Either execute a command by calling the shell or start a new shell
4749 */
4750 int
4751mch_call_shell(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004752 char_u *cmd,
4753 int options) /* SHELL_*, see vim.h */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004754{
4755 int x = 0;
4756 int tmode = cur_tmode;
4757#ifdef FEAT_TITLE
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004758 char szShellTitle[512];
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004759# ifdef FEAT_MBYTE
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004760 int did_set_title = FALSE;
4761
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004762 /* Change the title to reflect that we are in a subshell. */
4763 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4764 {
4765 WCHAR szShellTitle[512];
4766
4767 if (GetConsoleTitleW(szShellTitle,
4768 sizeof(szShellTitle)/sizeof(WCHAR) - 4) > 0)
4769 {
4770 if (cmd == NULL)
4771 wcscat(szShellTitle, L" :sh");
4772 else
4773 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004774 WCHAR *wn = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004775
4776 if (wn != NULL)
4777 {
4778 wcscat(szShellTitle, L" - !");
4779 if ((wcslen(szShellTitle) + wcslen(wn) <
4780 sizeof(szShellTitle)/sizeof(WCHAR)))
4781 wcscat(szShellTitle, wn);
4782 SetConsoleTitleW(szShellTitle);
4783 vim_free(wn);
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004784 did_set_title = TRUE;
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004785 }
4786 }
4787 }
4788 }
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004789 if (!did_set_title)
4790# endif
4791 /* Change the title to reflect that we are in a subshell. */
4792 if (GetConsoleTitle(szShellTitle, sizeof(szShellTitle) - 4) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004793 {
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004794 if (cmd == NULL)
4795 strcat(szShellTitle, " :sh");
4796 else
4797 {
4798 strcat(szShellTitle, " - !");
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004799 if ((strlen(szShellTitle) + strlen((char *)cmd)
4800 < sizeof(szShellTitle)))
4801 strcat(szShellTitle, (char *)cmd);
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004802 }
4803 SetConsoleTitle(szShellTitle);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004804 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004805#endif
4806
4807 out_flush();
4808
4809#ifdef MCH_WRITE_DUMP
4810 if (fdDump)
4811 {
4812 fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
4813 fflush(fdDump);
4814 }
4815#endif
4816
4817 /*
4818 * Catch all deadly signals while running the external command, because a
4819 * CTRL-C, Ctrl-Break or illegal instruction might otherwise kill us.
4820 */
4821 signal(SIGINT, SIG_IGN);
4822#if defined(__GNUC__) && !defined(__MINGW32__)
4823 signal(SIGKILL, SIG_IGN);
4824#else
4825 signal(SIGBREAK, SIG_IGN);
4826#endif
4827 signal(SIGILL, SIG_IGN);
4828 signal(SIGFPE, SIG_IGN);
4829 signal(SIGSEGV, SIG_IGN);
4830 signal(SIGTERM, SIG_IGN);
4831 signal(SIGABRT, SIG_IGN);
4832
4833 if (options & SHELL_COOKED)
4834 settmode(TMODE_COOK); /* set to normal mode */
4835
4836 if (cmd == NULL)
4837 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004838 x = mch_system((char *)p_sh, options);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004839 }
4840 else
4841 {
4842 /* we use "command" or "cmd" to start the shell; slow but easy */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004843 char_u *newcmd = NULL;
4844 char_u *cmdbase = cmd;
4845 long_u cmdlen;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004846
4847 /* Skip a leading ", ( and "(. */
4848 if (*cmdbase == '"' )
4849 ++cmdbase;
4850 if (*cmdbase == '(')
4851 ++cmdbase;
4852
4853 if ((STRNICMP(cmdbase, "start", 5) == 0) && vim_iswhite(cmdbase[5]))
4854 {
4855 STARTUPINFO si;
4856 PROCESS_INFORMATION pi;
4857 DWORD flags = CREATE_NEW_CONSOLE;
4858 char_u *p;
4859
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01004860 ZeroMemory(&si, sizeof(si));
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004861 si.cb = sizeof(si);
4862 si.lpReserved = NULL;
4863 si.lpDesktop = NULL;
4864 si.lpTitle = NULL;
4865 si.dwFlags = 0;
4866 si.cbReserved2 = 0;
4867 si.lpReserved2 = NULL;
4868
4869 cmdbase = skipwhite(cmdbase + 5);
4870 if ((STRNICMP(cmdbase, "/min", 4) == 0)
4871 && vim_iswhite(cmdbase[4]))
4872 {
4873 cmdbase = skipwhite(cmdbase + 4);
4874 si.dwFlags = STARTF_USESHOWWINDOW;
4875 si.wShowWindow = SW_SHOWMINNOACTIVE;
4876 }
4877 else if ((STRNICMP(cmdbase, "/b", 2) == 0)
4878 && vim_iswhite(cmdbase[2]))
4879 {
4880 cmdbase = skipwhite(cmdbase + 2);
4881 flags = CREATE_NO_WINDOW;
4882 si.dwFlags = STARTF_USESTDHANDLES;
4883 si.hStdInput = CreateFile("\\\\.\\NUL", // File name
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004884 GENERIC_READ, // Access flags
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004885 0, // Share flags
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004886 NULL, // Security att.
4887 OPEN_EXISTING, // Open flags
4888 FILE_ATTRIBUTE_NORMAL, // File att.
4889 NULL); // Temp file
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004890 si.hStdOutput = si.hStdInput;
4891 si.hStdError = si.hStdInput;
4892 }
4893
4894 /* Remove a trailing ", ) and )" if they have a match
4895 * at the start of the command. */
4896 if (cmdbase > cmd)
4897 {
4898 p = cmdbase + STRLEN(cmdbase);
4899 if (p > cmdbase && p[-1] == '"' && *cmd == '"')
4900 *--p = NUL;
4901 if (p > cmdbase && p[-1] == ')'
4902 && (*cmd =='(' || cmd[1] == '('))
4903 *--p = NUL;
4904 }
4905
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004906 newcmd = cmdbase;
4907 unescape_shellxquote(cmdbase, p_sxe);
4908
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004909 /*
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004910 * If creating new console, arguments are passed to the
4911 * 'cmd.exe' as-is. If it's not, arguments are not treated
4912 * correctly for current 'cmd.exe'. So unescape characters in
4913 * shellxescape except '|' for avoiding to be treated as
4914 * argument to them. Pass the arguments to sub-shell.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004915 */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004916 if (flags != CREATE_NEW_CONSOLE)
4917 {
4918 char_u *subcmd;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004919 char_u *cmd_shell = mch_getenv("COMSPEC");
4920
4921 if (cmd_shell == NULL || *cmd_shell == NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004922 cmd_shell = (char_u *)default_shell();
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004923
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004924 subcmd = vim_strsave_escaped_ext(cmdbase,
4925 (char_u *)"|", '^', FALSE);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004926 if (subcmd != NULL)
4927 {
4928 /* make "cmd.exe /c arguments" */
4929 cmdlen = STRLEN(cmd_shell) + STRLEN(subcmd) + 5;
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004930 newcmd = lalloc(cmdlen, TRUE);
4931 if (newcmd != NULL)
4932 vim_snprintf((char *)newcmd, cmdlen, "%s /c %s",
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004933 cmd_shell, subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004934 else
4935 newcmd = cmdbase;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004936 vim_free(subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004937 }
4938 }
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004939
4940 /*
4941 * Now, start the command as a process, so that it doesn't
4942 * inherit our handles which causes unpleasant dangling swap
4943 * files if we exit before the spawned process
4944 */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004945 if (vim_create_process((char *)newcmd, FALSE, flags, &si, &pi))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004946 x = 0;
4947 else
4948 {
4949 x = -1;
4950#ifdef FEAT_GUI_W32
4951 EMSG(_("E371: Command not found"));
4952#endif
4953 }
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004954
4955 if (newcmd != cmdbase)
4956 vim_free(newcmd);
4957
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01004958 if (si.dwFlags == STARTF_USESTDHANDLES && si.hStdInput != NULL)
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004959 {
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01004960 /* Close the handle to \\.\NUL created above. */
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004961 CloseHandle(si.hStdInput);
4962 }
4963 /* Close the handles to the subprocess, so that it goes away */
4964 CloseHandle(pi.hThread);
4965 CloseHandle(pi.hProcess);
4966 }
4967 else
4968 {
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004969 cmdlen = (
Bram Moolenaar071d4272004-06-13 20:20:40 +00004970#ifdef FEAT_GUI_W32
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004971 (allowPiping && !p_stmp ? 0 : STRLEN(vimrun_path)) +
Bram Moolenaar071d4272004-06-13 20:20:40 +00004972#endif
Bram Moolenaar0fde2902008-03-16 13:54:13 +00004973 STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10);
4974
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004975 newcmd = lalloc(cmdlen, TRUE);
4976 if (newcmd != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004977 {
4978#if defined(FEAT_GUI_W32)
4979 if (need_vimrun_warning)
4980 {
4981 MessageBox(NULL,
4982 _("VIMRUN.EXE not found in your $PATH.\n"
4983 "External commands will not pause after completion.\n"
4984 "See :help win32-vimrun for more information."),
4985 _("Vim Warning"),
4986 MB_ICONWARNING);
4987 need_vimrun_warning = FALSE;
4988 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004989 if (!s_dont_use_vimrun && (!allowPiping || p_stmp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004990 /* Use vimrun to execute the command. It opens a console
4991 * window, which can be closed without killing Vim. */
Bram Moolenaarcc448b32010-07-14 16:52:17 +02004992 vim_snprintf((char *)newcmd, cmdlen, "%s%s%s %s %s",
Bram Moolenaar071d4272004-06-13 20:20:40 +00004993 vimrun_path,
4994 (msg_silent != 0 || (options & SHELL_DOOUT))
4995 ? "-s " : "",
4996 p_sh, p_shcf, cmd);
4997 else
4998#endif
Bram Moolenaarcc448b32010-07-14 16:52:17 +02004999 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s",
Bram Moolenaar0fde2902008-03-16 13:54:13 +00005000 p_sh, p_shcf, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005001 x = mch_system((char *)newcmd, options);
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005002 vim_free(newcmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005004 }
5005 }
5006
5007 if (tmode == TMODE_RAW)
5008 settmode(TMODE_RAW); /* set to raw mode */
5009
5010 /* Print the return value, unless "vimrun" was used. */
5011 if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent
5012#if defined(FEAT_GUI_W32)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005013 && ((options & SHELL_DOOUT) || s_dont_use_vimrun
5014 || (allowPiping && !p_stmp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005015#endif
5016 )
5017 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005018 smsg((char_u *)_("shell returned %d"), x);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005019 msg_putchar('\n');
5020 }
5021#ifdef FEAT_TITLE
5022 resettitle();
5023#endif
5024
5025 signal(SIGINT, SIG_DFL);
5026#if defined(__GNUC__) && !defined(__MINGW32__)
5027 signal(SIGKILL, SIG_DFL);
5028#else
5029 signal(SIGBREAK, SIG_DFL);
5030#endif
5031 signal(SIGILL, SIG_DFL);
5032 signal(SIGFPE, SIG_DFL);
5033 signal(SIGSEGV, SIG_DFL);
5034 signal(SIGTERM, SIG_DFL);
5035 signal(SIGABRT, SIG_DFL);
5036
5037 return x;
5038}
5039
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005040#if defined(FEAT_JOB) || defined(PROTO)
5041 void
Bram Moolenaar9a6e33a2016-02-16 19:25:12 +01005042mch_start_job(char *cmd, job_T *job, jobopt_T *options)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005043{
5044 STARTUPINFO si;
5045 PROCESS_INFORMATION pi;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005046 HANDLE jo;
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005047# ifdef FEAT_CHANNEL
5048 channel_T *channel;
Bram Moolenaard8070362016-02-15 21:56:54 +01005049 HANDLE ifd[2];
5050 HANDLE ofd[2];
5051 HANDLE efd[2];
5052 SECURITY_ATTRIBUTES saAttr;
5053
5054 ifd[0] = INVALID_HANDLE_VALUE;
5055 ifd[1] = INVALID_HANDLE_VALUE;
5056 ofd[0] = INVALID_HANDLE_VALUE;
5057 ofd[1] = INVALID_HANDLE_VALUE;
5058 efd[0] = INVALID_HANDLE_VALUE;
5059 efd[1] = INVALID_HANDLE_VALUE;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005060
5061 channel = add_channel();
5062 if (channel == NULL)
5063 return;
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005064# endif
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005065
Bram Moolenaar76467df2016-02-12 19:30:26 +01005066 jo = CreateJobObject(NULL, NULL);
5067 if (jo == NULL)
5068 {
5069 job->jv_status = JOB_FAILED;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005070 goto failed;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005071 }
5072
5073 ZeroMemory(&pi, sizeof(pi));
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005074 ZeroMemory(&si, sizeof(si));
5075 si.cb = sizeof(si);
Bram Moolenaard8070362016-02-15 21:56:54 +01005076 si.dwFlags |= STARTF_USESHOWWINDOW;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005077 si.wShowWindow = SW_HIDE;
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005078
Bram Moolenaar5d54a042016-02-16 16:39:51 +01005079# ifdef FEAT_CHANNEL
Bram Moolenaard8070362016-02-15 21:56:54 +01005080 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
5081 saAttr.bInheritHandle = TRUE;
5082 saAttr.lpSecurityDescriptor = NULL;
5083 if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
5084 || !pSetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)
5085 || !CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
5086 || !pSetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)
5087 || !CreatePipe(&efd[0], &efd[1], &saAttr, 0)
5088 || !pSetHandleInformation(efd[0], HANDLE_FLAG_INHERIT, 0))
5089 goto failed;
5090 si.dwFlags |= STARTF_USESTDHANDLES;
5091 si.hStdInput = ifd[0];
5092 si.hStdOutput = ofd[1];
5093 si.hStdError = efd[1];
Bram Moolenaar5d54a042016-02-16 16:39:51 +01005094# endif
Bram Moolenaard8070362016-02-15 21:56:54 +01005095
5096 if (!vim_create_process(cmd, TRUE,
Bram Moolenaar76467df2016-02-12 19:30:26 +01005097 CREATE_SUSPENDED |
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005098 CREATE_DEFAULT_ERROR_MODE |
5099 CREATE_NEW_PROCESS_GROUP |
Bram Moolenaar76467df2016-02-12 19:30:26 +01005100 CREATE_NEW_CONSOLE,
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005101 &si, &pi))
Bram Moolenaar76467df2016-02-12 19:30:26 +01005102 {
5103 CloseHandle(jo);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005104 job->jv_status = JOB_FAILED;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005105 goto failed;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005106 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005107
5108 if (!AssignProcessToJobObject(jo, pi.hProcess))
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005109 {
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005110 /* if failing, switch the way to terminate
5111 * process with TerminateProcess. */
5112 CloseHandle(jo);
5113 jo = NULL;
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005114 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005115 ResumeThread(pi.hThread);
5116 CloseHandle(job->jv_proc_info.hThread);
5117 job->jv_proc_info = pi;
5118 job->jv_job_object = jo;
5119 job->jv_status = JOB_STARTED;
5120
Bram Moolenaar5d54a042016-02-16 16:39:51 +01005121# ifdef FEAT_CHANNEL
Bram Moolenaard8070362016-02-15 21:56:54 +01005122 CloseHandle(ifd[0]);
5123 CloseHandle(ofd[1]);
5124 CloseHandle(efd[1]);
5125
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005126 job->jv_channel = channel;
Bram Moolenaard8070362016-02-15 21:56:54 +01005127 channel_set_pipes(channel, (sock_T)ifd[1], (sock_T)ofd[0], (sock_T)efd[0]);
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005128 channel_set_job(channel, job);
Bram Moolenaar9a6e33a2016-02-16 19:25:12 +01005129 channel_set_mode(channel, options->jo_mode);
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005130
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005131# ifdef FEAT_GUI
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005132 channel_gui_register(channel);
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005133# endif
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005134# endif
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005135 return;
5136
5137failed:
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005138# ifdef FEAT_CHANNEL
Bram Moolenaard8070362016-02-15 21:56:54 +01005139 CloseHandle(ifd[0]);
5140 CloseHandle(ofd[0]);
5141 CloseHandle(efd[0]);
5142 CloseHandle(ifd[1]);
5143 CloseHandle(ofd[1]);
5144 CloseHandle(efd[1]);
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005145 channel_free(channel);
Bram Moolenaar71b0f7b2016-02-15 12:44:20 +01005146# else
5147 ; /* make compiler happy */
Bram Moolenaar16eb4f82016-02-14 23:02:34 +01005148# endif
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005149}
5150
5151 char *
5152mch_job_status(job_T *job)
5153{
5154 DWORD dwExitCode = 0;
5155
Bram Moolenaar76467df2016-02-12 19:30:26 +01005156 if (!GetExitCodeProcess(job->jv_proc_info.hProcess, &dwExitCode)
5157 || dwExitCode != STILL_ACTIVE)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005158 {
Bram Moolenaar76467df2016-02-12 19:30:26 +01005159 job->jv_status = JOB_ENDED;
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005160 return "dead";
5161 }
5162 return "run";
5163}
5164
5165 int
5166mch_stop_job(job_T *job, char_u *how)
5167{
Bram Moolenaar76467df2016-02-12 19:30:26 +01005168 int ret = 0;
5169 int ctrl_c = STRCMP(how, "int") == 0;
5170
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005171 if (STRCMP(how, "kill") == 0)
Bram Moolenaar76467df2016-02-12 19:30:26 +01005172 {
5173 if (job->jv_job_object != NULL)
5174 return TerminateJobObject(job->jv_job_object, 0) ? OK : FAIL;
5175 else
5176 return TerminateProcess(job->jv_proc_info.hProcess, 0) ? OK : FAIL;
5177 }
5178
5179 if (!AttachConsole(job->jv_proc_info.dwProcessId))
5180 return FAIL;
5181 ret = GenerateConsoleCtrlEvent(
5182 ctrl_c ? CTRL_C_EVENT : CTRL_BREAK_EVENT,
5183 job->jv_proc_info.dwProcessId)
5184 ? OK : FAIL;
5185 FreeConsole();
5186 return ret;
5187}
5188
5189/*
5190 * Clear the data related to "job".
5191 */
5192 void
5193mch_clear_job(job_T *job)
5194{
5195 if (job->jv_status != JOB_FAILED)
5196 {
5197 if (job->jv_job_object != NULL)
5198 CloseHandle(job->jv_job_object);
5199 CloseHandle(job->jv_proc_info.hProcess);
5200 }
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005201}
5202#endif
5203
Bram Moolenaar071d4272004-06-13 20:20:40 +00005204
5205#ifndef FEAT_GUI_W32
5206
5207/*
5208 * Start termcap mode
5209 */
5210 static void
5211termcap_mode_start(void)
5212{
5213 DWORD cmodein;
5214
5215 if (g_fTermcapMode)
5216 return;
5217
5218 SaveConsoleBuffer(&g_cbNonTermcap);
5219
5220 if (g_cbTermcap.IsValid)
5221 {
5222 /*
5223 * We've been in termcap mode before. Restore certain screen
5224 * characteristics, including the buffer size and the window
5225 * size. Since we will be redrawing the screen, we don't need
5226 * to restore the actual contents of the buffer.
5227 */
5228 RestoreConsoleBuffer(&g_cbTermcap, FALSE);
5229 SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow);
5230 Rows = g_cbTermcap.Info.dwSize.Y;
5231 Columns = g_cbTermcap.Info.dwSize.X;
5232 }
5233 else
5234 {
5235 /*
5236 * This is our first time entering termcap mode. Clear the console
5237 * screen buffer, and resize the buffer to match the current window
5238 * size. We will use this as the size of our editing environment.
5239 */
5240 ClearConsoleBuffer(g_attrCurrent);
5241 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
5242 }
5243
5244#ifdef FEAT_TITLE
5245 resettitle();
5246#endif
5247
5248 GetConsoleMode(g_hConIn, &cmodein);
5249#ifdef FEAT_MOUSE
5250 if (g_fMouseActive)
5251 cmodein |= ENABLE_MOUSE_INPUT;
5252 else
5253 cmodein &= ~ENABLE_MOUSE_INPUT;
5254#endif
5255 cmodein |= ENABLE_WINDOW_INPUT;
5256 SetConsoleMode(g_hConIn, cmodein);
5257
5258 redraw_later_clear();
5259 g_fTermcapMode = TRUE;
5260}
5261
5262
5263/*
5264 * End termcap mode
5265 */
5266 static void
5267termcap_mode_end(void)
5268{
5269 DWORD cmodein;
5270 ConsoleBuffer *cb;
5271 COORD coord;
5272 DWORD dwDummy;
5273
5274 if (!g_fTermcapMode)
5275 return;
5276
5277 SaveConsoleBuffer(&g_cbTermcap);
5278
5279 GetConsoleMode(g_hConIn, &cmodein);
5280 cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
5281 SetConsoleMode(g_hConIn, cmodein);
5282
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01005283#ifdef FEAT_RESTORE_ORIG_SCREEN
5284 cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
5285#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005286 cb = &g_cbNonTermcap;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01005287#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005288 RestoreConsoleBuffer(cb, p_rs);
5289 SetConsoleCursorInfo(g_hConOut, &g_cci);
5290
5291 if (p_rs || exiting)
5292 {
5293 /*
5294 * Clear anything that happens to be on the current line.
5295 */
5296 coord.X = 0;
5297 coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1));
5298 FillConsoleOutputCharacter(g_hConOut, ' ',
5299 cb->Info.dwSize.X, coord, &dwDummy);
5300 /*
5301 * The following is just for aesthetics. If we are exiting without
5302 * restoring the screen, then we want to have a prompt string
5303 * appear at the bottom line. However, the command interpreter
5304 * seems to always advance the cursor one line before displaying
5305 * the prompt string, which causes the screen to scroll. To
5306 * counter this, move the cursor up one line before exiting.
5307 */
5308 if (exiting && !p_rs)
5309 coord.Y--;
5310 /*
5311 * Position the cursor at the leftmost column of the desired row.
5312 */
5313 SetConsoleCursorPosition(g_hConOut, coord);
5314 }
5315
5316 g_fTermcapMode = FALSE;
5317}
5318#endif /* FEAT_GUI_W32 */
5319
5320
5321#ifdef FEAT_GUI_W32
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00005322/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00005323 void
5324mch_write(
5325 char_u *s,
5326 int len)
5327{
5328 /* never used */
5329}
5330
5331#else
5332
5333/*
5334 * clear `n' chars, starting from `coord'
5335 */
5336 static void
5337clear_chars(
5338 COORD coord,
5339 DWORD n)
5340{
5341 DWORD dwDummy;
5342
5343 FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
5344 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy);
5345}
5346
5347
5348/*
5349 * Clear the screen
5350 */
5351 static void
5352clear_screen(void)
5353{
5354 g_coord.X = g_coord.Y = 0;
5355 clear_chars(g_coord, Rows * Columns);
5356}
5357
5358
5359/*
5360 * Clear to end of display
5361 */
5362 static void
5363clear_to_end_of_display(void)
5364{
5365 clear_chars(g_coord, (Rows - g_coord.Y - 1)
5366 * Columns + (Columns - g_coord.X));
5367}
5368
5369
5370/*
5371 * Clear to end of line
5372 */
5373 static void
5374clear_to_end_of_line(void)
5375{
5376 clear_chars(g_coord, Columns - g_coord.X);
5377}
5378
5379
5380/*
5381 * Scroll the scroll region up by `cLines' lines
5382 */
5383 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005384scroll(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005385{
5386 COORD oldcoord = g_coord;
5387
5388 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
5389 delete_lines(cLines);
5390
5391 g_coord = oldcoord;
5392}
5393
5394
5395/*
5396 * Set the scroll region
5397 */
5398 static void
5399set_scroll_region(
5400 unsigned left,
5401 unsigned top,
5402 unsigned right,
5403 unsigned bottom)
5404{
5405 if (left >= right
5406 || top >= bottom
5407 || right > (unsigned) Columns - 1
5408 || bottom > (unsigned) Rows - 1)
5409 return;
5410
5411 g_srScrollRegion.Left = left;
5412 g_srScrollRegion.Top = top;
5413 g_srScrollRegion.Right = right;
5414 g_srScrollRegion.Bottom = bottom;
5415}
5416
5417
5418/*
5419 * Insert `cLines' lines at the current cursor position
5420 */
5421 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005422insert_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005423{
5424 SMALL_RECT source;
5425 COORD dest;
5426 CHAR_INFO fill;
5427
5428 dest.X = 0;
5429 dest.Y = g_coord.Y + cLines;
5430
5431 source.Left = 0;
5432 source.Top = g_coord.Y;
5433 source.Right = g_srScrollRegion.Right;
5434 source.Bottom = g_srScrollRegion.Bottom - cLines;
5435
5436 fill.Char.AsciiChar = ' ';
5437 fill.Attributes = g_attrCurrent;
5438
5439 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
5440
5441 /* Here we have to deal with a win32 console flake: If the scroll
5442 * region looks like abc and we scroll c to a and fill with d we get
5443 * cbd... if we scroll block c one line at a time to a, we get cdd...
5444 * vim expects cdd consistently... So we have to deal with that
5445 * here... (this also occurs scrolling the same way in the other
5446 * direction). */
5447
5448 if (source.Bottom < dest.Y)
5449 {
5450 COORD coord;
5451
5452 coord.X = 0;
5453 coord.Y = source.Bottom;
5454 clear_chars(coord, Columns * (dest.Y - source.Bottom));
5455 }
5456}
5457
5458
5459/*
5460 * Delete `cLines' lines at the current cursor position
5461 */
5462 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005463delete_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005464{
5465 SMALL_RECT source;
5466 COORD dest;
5467 CHAR_INFO fill;
5468 int nb;
5469
5470 dest.X = 0;
5471 dest.Y = g_coord.Y;
5472
5473 source.Left = 0;
5474 source.Top = g_coord.Y + cLines;
5475 source.Right = g_srScrollRegion.Right;
5476 source.Bottom = g_srScrollRegion.Bottom;
5477
5478 fill.Char.AsciiChar = ' ';
5479 fill.Attributes = g_attrCurrent;
5480
5481 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
5482
5483 /* Here we have to deal with a win32 console flake: If the scroll
5484 * region looks like abc and we scroll c to a and fill with d we get
5485 * cbd... if we scroll block c one line at a time to a, we get cdd...
5486 * vim expects cdd consistently... So we have to deal with that
5487 * here... (this also occurs scrolling the same way in the other
5488 * direction). */
5489
5490 nb = dest.Y + (source.Bottom - source.Top) + 1;
5491
5492 if (nb < source.Top)
5493 {
5494 COORD coord;
5495
5496 coord.X = 0;
5497 coord.Y = nb;
5498 clear_chars(coord, Columns * (source.Top - nb));
5499 }
5500}
5501
5502
5503/*
5504 * Set the cursor position
5505 */
5506 static void
5507gotoxy(
5508 unsigned x,
5509 unsigned y)
5510{
5511 if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows)
5512 return;
5513
5514 /* external cursor coords are 1-based; internal are 0-based */
5515 g_coord.X = x - 1;
5516 g_coord.Y = y - 1;
5517 SetConsoleCursorPosition(g_hConOut, g_coord);
5518}
5519
5520
5521/*
5522 * Set the current text attribute = (foreground | background)
5523 * See ../doc/os_win32.txt for the numbers.
5524 */
5525 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005526textattr(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005527{
Bram Moolenaar6383b922015-03-24 17:12:19 +01005528 g_attrCurrent = wAttr & 0xff;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005529
5530 SetConsoleTextAttribute(g_hConOut, wAttr);
5531}
5532
5533
5534 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005535textcolor(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536{
Bram Moolenaar6383b922015-03-24 17:12:19 +01005537 g_attrCurrent = (g_attrCurrent & 0xf0) + (wAttr & 0x0f);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005538
5539 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
5540}
5541
5542
5543 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005544textbackground(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005545{
Bram Moolenaar6383b922015-03-24 17:12:19 +01005546 g_attrCurrent = (g_attrCurrent & 0x0f) + ((wAttr & 0x0f) << 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005547
5548 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
5549}
5550
5551
5552/*
5553 * restore the default text attribute (whatever we started with)
5554 */
5555 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005556normvideo(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005557{
5558 textattr(g_attrDefault);
5559}
5560
5561
5562static WORD g_attrPreStandout = 0;
5563
5564/*
5565 * Make the text standout, by brightening it
5566 */
5567 static void
5568standout(void)
5569{
5570 g_attrPreStandout = g_attrCurrent;
5571 textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
5572}
5573
5574
5575/*
5576 * Turn off standout mode
5577 */
5578 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005579standend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005580{
5581 if (g_attrPreStandout)
5582 {
5583 textattr(g_attrPreStandout);
5584 g_attrPreStandout = 0;
5585 }
5586}
5587
5588
5589/*
Bram Moolenaarff1d0d42007-05-10 17:24:16 +00005590 * Set normal fg/bg color, based on T_ME. Called when t_me has been set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005591 */
5592 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005593mch_set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005594{
5595 char_u *p;
5596 int n;
5597
5598 cterm_normal_fg_color = (g_attrDefault & 0xf) + 1;
5599 cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1;
5600 if (T_ME[0] == ESC && T_ME[1] == '|')
5601 {
5602 p = T_ME + 2;
5603 n = getdigits(&p);
5604 if (*p == 'm' && n > 0)
5605 {
5606 cterm_normal_fg_color = (n & 0xf) + 1;
5607 cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
5608 }
5609 }
5610}
5611
5612
5613/*
5614 * visual bell: flash the screen
5615 */
5616 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005617visual_bell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005618{
5619 COORD coordOrigin = {0, 0};
5620 WORD attrFlash = ~g_attrCurrent & 0xff;
5621
5622 DWORD dwDummy;
5623 LPWORD oldattrs = (LPWORD)alloc(Rows * Columns * sizeof(WORD));
5624
5625 if (oldattrs == NULL)
5626 return;
5627 ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
5628 coordOrigin, &dwDummy);
5629 FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns,
5630 coordOrigin, &dwDummy);
5631
5632 Sleep(15); /* wait for 15 msec */
5633 WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
5634 coordOrigin, &dwDummy);
5635 vim_free(oldattrs);
5636}
5637
5638
5639/*
5640 * Make the cursor visible or invisible
5641 */
5642 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005643cursor_visible(BOOL fVisible)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005644{
5645 s_cursor_visible = fVisible;
5646#ifdef MCH_CURSOR_SHAPE
5647 mch_update_cursor();
5648#endif
5649}
5650
5651
5652/*
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005653 * write `cbToWrite' bytes in `pchBuf' to the screen
5654 * Returns the number of bytes actually written (at least one).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005655 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005656 static DWORD
Bram Moolenaar071d4272004-06-13 20:20:40 +00005657write_chars(
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005658 char_u *pchBuf,
5659 DWORD cbToWrite)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005660{
5661 COORD coord = g_coord;
5662 DWORD written;
5663
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005664#ifdef FEAT_MBYTE
5665 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
5666 {
5667 static WCHAR *unicodebuf = NULL;
5668 static int unibuflen = 0;
5669 int length;
5670 DWORD n, cchwritten, cells;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005671
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005672 length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite, 0, 0);
5673 if (unicodebuf == NULL || length > unibuflen)
5674 {
5675 vim_free(unicodebuf);
5676 unicodebuf = (WCHAR *)lalloc(length * sizeof(WCHAR), FALSE);
5677 unibuflen = length;
5678 }
5679 MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite,
5680 unicodebuf, unibuflen);
5681
5682 cells = mb_string2cells(pchBuf, cbToWrite);
5683 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cells,
5684 coord, &written);
5685 /* When writing fails or didn't write a single character, pretend one
5686 * character was written, otherwise we get stuck. */
5687 if (WriteConsoleOutputCharacterW(g_hConOut, unicodebuf, length,
5688 coord, &cchwritten) == 0
5689 || cchwritten == 0)
5690 cchwritten = 1;
5691
5692 if (cchwritten == length)
5693 {
5694 written = cbToWrite;
5695 g_coord.X += (SHORT)cells;
5696 }
5697 else
5698 {
5699 char_u *p = pchBuf;
5700 for (n = 0; n < cchwritten; n++)
5701 mb_cptr_adv(p);
5702 written = p - pchBuf;
5703 g_coord.X += (SHORT)mb_string2cells(pchBuf, written);
5704 }
5705 }
5706 else
5707#endif
5708 {
5709 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cbToWrite,
5710 coord, &written);
5711 /* When writing fails or didn't write a single character, pretend one
5712 * character was written, otherwise we get stuck. */
5713 if (WriteConsoleOutputCharacter(g_hConOut, (LPCSTR)pchBuf, cbToWrite,
5714 coord, &written) == 0
5715 || written == 0)
5716 written = 1;
5717
5718 g_coord.X += (SHORT) written;
5719 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005720
5721 while (g_coord.X > g_srScrollRegion.Right)
5722 {
5723 g_coord.X -= (SHORT) Columns;
5724 if (g_coord.Y < g_srScrollRegion.Bottom)
5725 g_coord.Y++;
5726 }
5727
5728 gotoxy(g_coord.X + 1, g_coord.Y + 1);
5729
5730 return written;
5731}
5732
5733
5734/*
5735 * mch_write(): write the output buffer to the screen, translating ESC
5736 * sequences into calls to console output routines.
5737 */
5738 void
5739mch_write(
5740 char_u *s,
5741 int len)
5742{
5743 s[len] = NUL;
5744
5745 if (!term_console)
5746 {
5747 write(1, s, (unsigned)len);
5748 return;
5749 }
5750
5751 /* translate ESC | sequences into faked bios calls */
5752 while (len--)
5753 {
5754 /* optimization: use one single write_chars for runs of text,
5755 * rather than once per character It ain't curses, but it helps. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005756 DWORD prefix = (DWORD)strcspn((char *)s, "\n\r\b\a\033");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005757
5758 if (p_wd)
5759 {
5760 WaitForChar(p_wd);
5761 if (prefix != 0)
5762 prefix = 1;
5763 }
5764
5765 if (prefix != 0)
5766 {
5767 DWORD nWritten;
5768
5769 nWritten = write_chars(s, prefix);
5770#ifdef MCH_WRITE_DUMP
5771 if (fdDump)
5772 {
5773 fputc('>', fdDump);
5774 fwrite(s, sizeof(char_u), nWritten, fdDump);
5775 fputs("<\n", fdDump);
5776 }
5777#endif
5778 len -= (nWritten - 1);
5779 s += nWritten;
5780 }
5781 else if (s[0] == '\n')
5782 {
5783 /* \n, newline: go to the beginning of the next line or scroll */
5784 if (g_coord.Y == g_srScrollRegion.Bottom)
5785 {
5786 scroll(1);
5787 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
5788 }
5789 else
5790 {
5791 gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
5792 }
5793#ifdef MCH_WRITE_DUMP
5794 if (fdDump)
5795 fputs("\\n\n", fdDump);
5796#endif
5797 s++;
5798 }
5799 else if (s[0] == '\r')
5800 {
5801 /* \r, carriage return: go to beginning of line */
5802 gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
5803#ifdef MCH_WRITE_DUMP
5804 if (fdDump)
5805 fputs("\\r\n", fdDump);
5806#endif
5807 s++;
5808 }
5809 else if (s[0] == '\b')
5810 {
5811 /* \b, backspace: move cursor one position left */
5812 if (g_coord.X > g_srScrollRegion.Left)
5813 g_coord.X--;
5814 else if (g_coord.Y > g_srScrollRegion.Top)
5815 {
5816 g_coord.X = g_srScrollRegion.Right;
5817 g_coord.Y--;
5818 }
5819 gotoxy(g_coord.X + 1, g_coord.Y + 1);
5820#ifdef MCH_WRITE_DUMP
5821 if (fdDump)
5822 fputs("\\b\n", fdDump);
5823#endif
5824 s++;
5825 }
5826 else if (s[0] == '\a')
5827 {
5828 /* \a, bell */
5829 MessageBeep(0xFFFFFFFF);
5830#ifdef MCH_WRITE_DUMP
5831 if (fdDump)
5832 fputs("\\a\n", fdDump);
5833#endif
5834 s++;
5835 }
5836 else if (s[0] == ESC && len >= 3-1 && s[1] == '|')
5837 {
5838#ifdef MCH_WRITE_DUMP
Bram Moolenaarc0197e22004-09-13 20:26:32 +00005839 char_u *old_s = s;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005840#endif
Bram Moolenaarc0197e22004-09-13 20:26:32 +00005841 char_u *p;
5842 int arg1 = 0, arg2 = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005843
5844 switch (s[2])
5845 {
5846 /* one or two numeric arguments, separated by ';' */
5847
5848 case '0': case '1': case '2': case '3': case '4':
5849 case '5': case '6': case '7': case '8': case '9':
5850 p = s + 2;
5851 arg1 = getdigits(&p); /* no check for length! */
5852 if (p > s + len)
5853 break;
5854
5855 if (*p == ';')
5856 {
5857 ++p;
5858 arg2 = getdigits(&p); /* no check for length! */
5859 if (p > s + len)
5860 break;
5861
5862 if (*p == 'H')
5863 gotoxy(arg2, arg1);
5864 else if (*p == 'r')
5865 set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
5866 }
5867 else if (*p == 'A')
5868 {
5869 /* move cursor up arg1 lines in same column */
5870 gotoxy(g_coord.X + 1,
5871 max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
5872 }
5873 else if (*p == 'C')
5874 {
5875 /* move cursor right arg1 columns in same line */
5876 gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
5877 g_coord.Y + 1);
5878 }
5879 else if (*p == 'H')
5880 {
5881 gotoxy(1, arg1);
5882 }
5883 else if (*p == 'L')
5884 {
5885 insert_lines(arg1);
5886 }
5887 else if (*p == 'm')
5888 {
5889 if (arg1 == 0)
5890 normvideo();
5891 else
5892 textattr((WORD) arg1);
5893 }
5894 else if (*p == 'f')
5895 {
5896 textcolor((WORD) arg1);
5897 }
5898 else if (*p == 'b')
5899 {
5900 textbackground((WORD) arg1);
5901 }
5902 else if (*p == 'M')
5903 {
5904 delete_lines(arg1);
5905 }
5906
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00005907 len -= (int)(p - s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005908 s = p + 1;
5909 break;
5910
5911
5912 /* Three-character escape sequences */
5913
5914 case 'A':
5915 /* move cursor up one line in same column */
5916 gotoxy(g_coord.X + 1,
5917 max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
5918 goto got3;
5919
5920 case 'B':
5921 visual_bell();
5922 goto got3;
5923
5924 case 'C':
5925 /* move cursor right one column in same line */
5926 gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
5927 g_coord.Y + 1);
5928 goto got3;
5929
5930 case 'E':
5931 termcap_mode_end();
5932 goto got3;
5933
5934 case 'F':
5935 standout();
5936 goto got3;
5937
5938 case 'f':
5939 standend();
5940 goto got3;
5941
5942 case 'H':
5943 gotoxy(1, 1);
5944 goto got3;
5945
5946 case 'j':
5947 clear_to_end_of_display();
5948 goto got3;
5949
5950 case 'J':
5951 clear_screen();
5952 goto got3;
5953
5954 case 'K':
5955 clear_to_end_of_line();
5956 goto got3;
5957
5958 case 'L':
5959 insert_lines(1);
5960 goto got3;
5961
5962 case 'M':
5963 delete_lines(1);
5964 goto got3;
5965
5966 case 'S':
5967 termcap_mode_start();
5968 goto got3;
5969
5970 case 'V':
5971 cursor_visible(TRUE);
5972 goto got3;
5973
5974 case 'v':
5975 cursor_visible(FALSE);
5976 goto got3;
5977
5978 got3:
5979 s += 3;
5980 len -= 2;
5981 }
5982
5983#ifdef MCH_WRITE_DUMP
5984 if (fdDump)
5985 {
5986 fputs("ESC | ", fdDump);
5987 fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
5988 fputc('\n', fdDump);
5989 }
5990#endif
5991 }
5992 else
5993 {
5994 /* Write a single character */
5995 DWORD nWritten;
5996
5997 nWritten = write_chars(s, 1);
5998#ifdef MCH_WRITE_DUMP
5999 if (fdDump)
6000 {
6001 fputc('>', fdDump);
6002 fwrite(s, sizeof(char_u), nWritten, fdDump);
6003 fputs("<\n", fdDump);
6004 }
6005#endif
6006
6007 len -= (nWritten - 1);
6008 s += nWritten;
6009 }
6010 }
6011
6012#ifdef MCH_WRITE_DUMP
6013 if (fdDump)
6014 fflush(fdDump);
6015#endif
6016}
6017
6018#endif /* FEAT_GUI_W32 */
6019
6020
6021/*
6022 * Delay for half a second.
6023 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00006024/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00006025 void
6026mch_delay(
6027 long msec,
6028 int ignoreinput)
6029{
6030#ifdef FEAT_GUI_W32
6031 Sleep((int)msec); /* never wait for input */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006032#else /* Console */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006033 if (ignoreinput)
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006034# ifdef FEAT_MZSCHEME
6035 if (mzthreads_allowed() && p_mzq > 0 && msec > p_mzq)
6036 {
6037 int towait = p_mzq;
6038
6039 /* if msec is large enough, wait by portions in p_mzq */
6040 while (msec > 0)
6041 {
6042 mzvim_check_threads();
6043 if (msec < towait)
6044 towait = msec;
6045 Sleep(towait);
6046 msec -= towait;
6047 }
6048 }
6049 else
6050# endif
6051 Sleep((int)msec);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006052 else
6053 WaitForChar(msec);
6054#endif
6055}
6056
6057
6058/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01006059 * This version of remove is not scared by a readonly (backup) file.
6060 * This can also remove a symbolic link like Unix.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006061 * Return 0 for success, -1 for failure.
6062 */
6063 int
6064mch_remove(char_u *name)
6065{
6066#ifdef FEAT_MBYTE
6067 WCHAR *wn = NULL;
6068 int n;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02006069#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006070
Bram Moolenaar203258c2016-01-17 22:15:16 +01006071 /*
6072 * On Windows, deleting a directory's symbolic link is done by
6073 * RemoveDirectory(): mch_rmdir. It seems unnatural, but it is fact.
6074 */
6075 if (mch_isdir(name) && mch_is_symbolic_link(name))
6076 return mch_rmdir(name);
6077
Bram Moolenaar12b559e2013-06-12 22:41:37 +02006078 win32_setattrs(name, FILE_ATTRIBUTE_NORMAL);
6079
6080#ifdef FEAT_MBYTE
Bram Moolenaar071d4272004-06-13 20:20:40 +00006081 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6082 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006083 wn = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006084 if (wn != NULL)
6085 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006086 n = DeleteFileW(wn) ? 0 : -1;
6087 vim_free(wn);
6088 if (n == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
6089 return n;
6090 /* Retry with non-wide function (for Windows 98). */
6091 }
6092 }
6093#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006094 return DeleteFile((LPCSTR)name) ? 0 : -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006095}
6096
6097
6098/*
6099 * check for an "interrupt signal": CTRL-break or CTRL-C
6100 */
6101 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006102mch_breakcheck(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006103{
6104#ifndef FEAT_GUI_W32 /* never used */
6105 if (g_fCtrlCPressed || g_fCBrkPressed)
6106 {
6107 g_fCtrlCPressed = g_fCBrkPressed = FALSE;
6108 got_int = TRUE;
6109 }
6110#endif
6111}
6112
Bram Moolenaaree273972016-01-02 21:11:51 +01006113/* physical RAM to leave for the OS */
6114#define WINNT_RESERVE_BYTES (256*1024*1024)
6115#define WIN95_RESERVE_BYTES (8*1024*1024)
6116
6117/*
6118 * How much main memory in KiB that can be used by VIM.
6119 */
6120/*ARGSUSED*/
6121 long_u
6122mch_total_mem(int special)
6123{
6124 PlatformId();
6125#if (defined(_MSC_VER) && (WINVER > 0x0400)) || defined(MEMORYSTATUSEX)
6126 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
6127 {
6128 MEMORYSTATUSEX ms;
6129
6130 /* Need to use GlobalMemoryStatusEx() when there is more memory than
6131 * what fits in 32 bits. But it's not always available. */
6132 ms.dwLength = sizeof(MEMORYSTATUSEX);
6133 GlobalMemoryStatusEx(&ms);
6134 if (ms.ullAvailVirtual < ms.ullTotalPhys)
6135 {
6136 /* Process address space fits in physical RAM, use all of it. */
6137 return (long_u)(ms.ullAvailVirtual / 1024);
6138 }
6139 if (ms.ullTotalPhys <= WINNT_RESERVE_BYTES)
6140 {
6141 /* Catch old NT box or perverse hardware setup. */
6142 return (long_u)((ms.ullTotalPhys / 2) / 1024);
6143 }
6144 /* Use physical RAM less reserve for OS + data. */
6145 return (long_u)((ms.ullTotalPhys - WINNT_RESERVE_BYTES) / 1024);
6146 }
6147 else
6148#endif
6149 {
6150 /* Pre-XP or 95 OS handling. */
6151 MEMORYSTATUS ms;
6152 long_u os_reserve_bytes;
6153
6154 ms.dwLength = sizeof(MEMORYSTATUS);
6155 GlobalMemoryStatus(&ms);
6156 if (ms.dwAvailVirtual < ms.dwTotalPhys)
6157 {
6158 /* Process address space fits in physical RAM, use all of it. */
6159 return (long_u)(ms.dwAvailVirtual / 1024);
6160 }
6161 os_reserve_bytes = (g_PlatformId == VER_PLATFORM_WIN32_NT)
6162 ? WINNT_RESERVE_BYTES
6163 : WIN95_RESERVE_BYTES;
6164 if (ms.dwTotalPhys <= os_reserve_bytes)
6165 {
6166 /* Catch old boxes or perverse hardware setup. */
6167 return (long_u)((ms.dwTotalPhys / 2) / 1024);
6168 }
6169 /* Use physical RAM less reserve for OS + data. */
6170 return (long_u)((ms.dwTotalPhys - os_reserve_bytes) / 1024);
6171 }
6172}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006173
Bram Moolenaar071d4272004-06-13 20:20:40 +00006174#ifdef FEAT_MBYTE
6175/*
6176 * Same code as below, but with wide functions and no comments.
6177 * Return 0 for success, non-zero for failure.
6178 */
6179 int
6180mch_wrename(WCHAR *wold, WCHAR *wnew)
6181{
6182 WCHAR *p;
6183 int i;
6184 WCHAR szTempFile[_MAX_PATH + 1];
6185 WCHAR szNewPath[_MAX_PATH + 1];
6186 HANDLE hf;
6187
6188 if (!mch_windows95())
6189 {
6190 p = wold;
6191 for (i = 0; wold[i] != NUL; ++i)
6192 if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':')
6193 && wold[i + 1] != 0)
6194 p = wold + i + 1;
6195 if ((int)(wold + i - p) < 8 || p[6] != '~')
6196 return (MoveFileW(wold, wnew) == 0);
6197 }
6198
6199 if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL)
6200 return -1;
6201 *p = NUL;
6202
6203 if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0)
6204 return -2;
6205
6206 if (!DeleteFileW(szTempFile))
6207 return -3;
6208
6209 if (!MoveFileW(wold, szTempFile))
6210 return -4;
6211
6212 if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW,
6213 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
6214 return -5;
6215 if (!CloseHandle(hf))
6216 return -6;
6217
6218 if (!MoveFileW(szTempFile, wnew))
6219 {
6220 (void)MoveFileW(szTempFile, wold);
6221 return -7;
6222 }
6223
6224 DeleteFileW(szTempFile);
6225
6226 if (!DeleteFileW(wold))
6227 return -8;
6228
6229 return 0;
6230}
6231#endif
6232
6233
6234/*
6235 * mch_rename() works around a bug in rename (aka MoveFile) in
6236 * Windows 95: rename("foo.bar", "foo.bar~") will generate a
6237 * file whose short file name is "FOO.BAR" (its long file name will
6238 * be correct: "foo.bar~"). Because a file can be accessed by
6239 * either its SFN or its LFN, "foo.bar" has effectively been
6240 * renamed to "foo.bar", which is not at all what was wanted. This
6241 * seems to happen only when renaming files with three-character
6242 * extensions by appending a suffix that does not include ".".
6243 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
6244 *
6245 * There is another problem, which isn't really a bug but isn't right either:
6246 * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be
6247 * "abcdef~1.txt" again. This has been reported on Windows NT 4.0 with
6248 * service pack 6. Doesn't seem to happen on Windows 98.
6249 *
6250 * Like rename(), returns 0 upon success, non-zero upon failure.
6251 * Should probably set errno appropriately when errors occur.
6252 */
6253 int
6254mch_rename(
6255 const char *pszOldFile,
6256 const char *pszNewFile)
6257{
6258 char szTempFile[_MAX_PATH+1];
6259 char szNewPath[_MAX_PATH+1];
6260 char *pszFilePart;
6261 HANDLE hf;
6262#ifdef FEAT_MBYTE
6263 WCHAR *wold = NULL;
6264 WCHAR *wnew = NULL;
6265 int retval = -1;
6266
6267 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6268 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006269 wold = enc_to_utf16((char_u *)pszOldFile, NULL);
6270 wnew = enc_to_utf16((char_u *)pszNewFile, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006271 if (wold != NULL && wnew != NULL)
6272 retval = mch_wrename(wold, wnew);
6273 vim_free(wold);
6274 vim_free(wnew);
6275 if (retval == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
6276 return retval;
6277 /* Retry with non-wide function (for Windows 98). */
6278 }
6279#endif
6280
6281 /*
6282 * No need to play tricks if not running Windows 95, unless the file name
6283 * contains a "~" as the seventh character.
6284 */
6285 if (!mch_windows95())
6286 {
6287 pszFilePart = (char *)gettail((char_u *)pszOldFile);
6288 if (STRLEN(pszFilePart) < 8 || pszFilePart[6] != '~')
6289 return rename(pszOldFile, pszNewFile);
6290 }
6291
6292 /* Get base path of new file name. Undocumented feature: If pszNewFile is
6293 * a directory, no error is returned and pszFilePart will be NULL. */
6294 if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0
6295 || pszFilePart == NULL)
6296 return -1;
6297 *pszFilePart = NUL;
6298
6299 /* Get (and create) a unique temporary file name in directory of new file */
6300 if (GetTempFileName(szNewPath, "VIM", 0, szTempFile) == 0)
6301 return -2;
6302
6303 /* blow the temp file away */
6304 if (!DeleteFile(szTempFile))
6305 return -3;
6306
6307 /* rename old file to the temp file */
6308 if (!MoveFile(pszOldFile, szTempFile))
6309 return -4;
6310
6311 /* now create an empty file called pszOldFile; this prevents the operating
6312 * system using pszOldFile as an alias (SFN) if we're renaming within the
6313 * same directory. For example, we're editing a file called
6314 * filename.asc.txt by its SFN, filena~1.txt. If we rename filena~1.txt
6315 * to filena~1.txt~ (i.e., we're making a backup while writing it), the
6316 * SFN for filena~1.txt~ will be filena~1.txt, by default, which will
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006317 * cause all sorts of problems later in buf_write(). So, we create an
6318 * empty file called filena~1.txt and the system will have to find some
6319 * other SFN for filena~1.txt~, such as filena~2.txt
Bram Moolenaar071d4272004-06-13 20:20:40 +00006320 */
6321 if ((hf = CreateFile(pszOldFile, GENERIC_WRITE, 0, NULL, CREATE_NEW,
6322 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
6323 return -5;
6324 if (!CloseHandle(hf))
6325 return -6;
6326
6327 /* rename the temp file to the new file */
6328 if (!MoveFile(szTempFile, pszNewFile))
6329 {
6330 /* Renaming failed. Rename the file back to its old name, so that it
6331 * looks like nothing happened. */
6332 (void)MoveFile(szTempFile, pszOldFile);
6333
6334 return -7;
6335 }
6336
6337 /* Seems to be left around on Novell filesystems */
6338 DeleteFile(szTempFile);
6339
6340 /* finally, remove the empty old file */
6341 if (!DeleteFile(pszOldFile))
6342 return -8;
6343
6344 return 0; /* success */
6345}
6346
6347/*
6348 * Get the default shell for the current hardware platform
6349 */
6350 char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006351default_shell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006352{
6353 char* psz = NULL;
6354
6355 PlatformId();
6356
6357 if (g_PlatformId == VER_PLATFORM_WIN32_NT) /* Windows NT */
6358 psz = "cmd.exe";
6359 else if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS) /* Windows 95 */
6360 psz = "command.com";
6361
6362 return psz;
6363}
6364
6365/*
6366 * mch_access() extends access() to do more detailed check on network drives.
6367 * Returns 0 if file "n" has access rights according to "p", -1 otherwise.
6368 */
6369 int
6370mch_access(char *n, int p)
6371{
6372 HANDLE hFile;
6373 DWORD am;
6374 int retval = -1; /* default: fail */
6375#ifdef FEAT_MBYTE
6376 WCHAR *wn = NULL;
6377
6378 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006379 wn = enc_to_utf16((char_u *)n, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006380#endif
6381
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006382 if (mch_isdir((char_u *)n))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006383 {
6384 char TempName[_MAX_PATH + 16] = "";
6385#ifdef FEAT_MBYTE
6386 WCHAR TempNameW[_MAX_PATH + 16] = L"";
6387#endif
6388
6389 if (p & R_OK)
6390 {
6391 /* Read check is performed by seeing if we can do a find file on
6392 * the directory for any file. */
6393#ifdef FEAT_MBYTE
6394 if (wn != NULL)
6395 {
6396 int i;
6397 WIN32_FIND_DATAW d;
6398
6399 for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i)
6400 TempNameW[i] = wn[i];
6401 if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/')
6402 TempNameW[i++] = '\\';
6403 TempNameW[i++] = '*';
6404 TempNameW[i++] = 0;
6405
6406 hFile = FindFirstFileW(TempNameW, &d);
6407 if (hFile == INVALID_HANDLE_VALUE)
6408 {
6409 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
6410 goto getout;
6411
6412 /* Retry with non-wide function (for Windows 98). */
6413 vim_free(wn);
6414 wn = NULL;
6415 }
6416 else
6417 (void)FindClose(hFile);
6418 }
6419 if (wn == NULL)
6420#endif
6421 {
6422 char *pch;
6423 WIN32_FIND_DATA d;
6424
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006425 vim_strncpy((char_u *)TempName, (char_u *)n, _MAX_PATH);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006426 pch = TempName + STRLEN(TempName) - 1;
6427 if (*pch != '\\' && *pch != '/')
6428 *++pch = '\\';
6429 *++pch = '*';
6430 *++pch = NUL;
6431
6432 hFile = FindFirstFile(TempName, &d);
6433 if (hFile == INVALID_HANDLE_VALUE)
6434 goto getout;
6435 (void)FindClose(hFile);
6436 }
6437 }
6438
6439 if (p & W_OK)
6440 {
6441 /* Trying to create a temporary file in the directory should catch
6442 * directories on read-only network shares. However, in
6443 * directories whose ACL allows writes but denies deletes will end
6444 * up keeping the temporary file :-(. */
6445#ifdef FEAT_MBYTE
6446 if (wn != NULL)
6447 {
6448 if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW))
6449 {
6450 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
6451 goto getout;
6452
6453 /* Retry with non-wide function (for Windows 98). */
6454 vim_free(wn);
6455 wn = NULL;
6456 }
6457 else
6458 DeleteFileW(TempNameW);
6459 }
6460 if (wn == NULL)
6461#endif
6462 {
6463 if (!GetTempFileName(n, "VIM", 0, TempName))
6464 goto getout;
6465 mch_remove((char_u *)TempName);
6466 }
6467 }
6468 }
6469 else
6470 {
6471 /* Trying to open the file for the required access does ACL, read-only
6472 * network share, and file attribute checks. */
6473 am = ((p & W_OK) ? GENERIC_WRITE : 0)
6474 | ((p & R_OK) ? GENERIC_READ : 0);
6475#ifdef FEAT_MBYTE
6476 if (wn != NULL)
6477 {
6478 hFile = CreateFileW(wn, am, 0, NULL, OPEN_EXISTING, 0, NULL);
6479 if (hFile == INVALID_HANDLE_VALUE
6480 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
6481 {
6482 /* Retry with non-wide function (for Windows 98). */
6483 vim_free(wn);
6484 wn = NULL;
6485 }
6486 }
6487 if (wn == NULL)
6488#endif
6489 hFile = CreateFile(n, am, 0, NULL, OPEN_EXISTING, 0, NULL);
6490 if (hFile == INVALID_HANDLE_VALUE)
6491 goto getout;
6492 CloseHandle(hFile);
6493 }
6494
6495 retval = 0; /* success */
6496getout:
6497#ifdef FEAT_MBYTE
6498 vim_free(wn);
6499#endif
6500 return retval;
6501}
6502
6503#if defined(FEAT_MBYTE) || defined(PROTO)
6504/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006505 * Version of open() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006506 */
6507 int
6508mch_open(char *name, int flags, int mode)
6509{
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006510 /* _wopen() does not work with Borland C 5.5: creates a read-only file. */
6511# ifndef __BORLANDC__
Bram Moolenaar071d4272004-06-13 20:20:40 +00006512 WCHAR *wn;
6513 int f;
6514
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006515 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006516 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006517 wn = enc_to_utf16((char_u *)name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006518 if (wn != NULL)
6519 {
6520 f = _wopen(wn, flags, mode);
6521 vim_free(wn);
Bram Moolenaarcd981f22014-02-11 17:06:00 +01006522 if (f >= 0 || g_PlatformId == VER_PLATFORM_WIN32_NT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523 return f;
6524 /* Retry with non-wide function (for Windows 98). Can't use
6525 * GetLastError() here and it's unclear what errno gets set to if
6526 * the _wopen() fails for missing wide functions. */
6527 }
6528 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006529# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006530
Bram Moolenaarf9e6c3b2014-11-05 18:36:03 +01006531 /* open() can open a file which name is longer than _MAX_PATH bytes
6532 * and shorter than _MAX_PATH characters successfully, but sometimes it
6533 * causes unexpected error in another part. We make it an error explicitly
6534 * here. */
6535 if (strlen(name) >= _MAX_PATH)
6536 return -1;
6537
Bram Moolenaar071d4272004-06-13 20:20:40 +00006538 return open(name, flags, mode);
6539}
6540
6541/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006542 * Version of fopen() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006543 */
6544 FILE *
6545mch_fopen(char *name, char *mode)
6546{
6547 WCHAR *wn, *wm;
6548 FILE *f = NULL;
6549
6550 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
6551# ifdef __BORLANDC__
6552 /* Wide functions of Borland C 5.5 do not work on Windows 98. */
6553 && g_PlatformId == VER_PLATFORM_WIN32_NT
6554# endif
6555 )
6556 {
Bram Moolenaare6a91fd2008-07-24 18:51:11 +00006557# if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0fde2902008-03-16 13:54:13 +00006558 /* Work around an annoying assertion in the Microsoft debug CRT
6559 * when mode's text/binary setting doesn't match _get_fmode(). */
6560 char newMode = mode[strlen(mode) - 1];
6561 int oldMode = 0;
6562
6563 _get_fmode(&oldMode);
6564 if (newMode == 't')
6565 _set_fmode(_O_TEXT);
6566 else if (newMode == 'b')
6567 _set_fmode(_O_BINARY);
6568# endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006569 wn = enc_to_utf16((char_u *)name, NULL);
6570 wm = enc_to_utf16((char_u *)mode, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006571 if (wn != NULL && wm != NULL)
6572 f = _wfopen(wn, wm);
6573 vim_free(wn);
6574 vim_free(wm);
Bram Moolenaar0fde2902008-03-16 13:54:13 +00006575
Bram Moolenaare6a91fd2008-07-24 18:51:11 +00006576# if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0fde2902008-03-16 13:54:13 +00006577 _set_fmode(oldMode);
6578# endif
6579
Bram Moolenaarcd981f22014-02-11 17:06:00 +01006580 if (f != NULL || g_PlatformId == VER_PLATFORM_WIN32_NT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006581 return f;
6582 /* Retry with non-wide function (for Windows 98). Can't use
6583 * GetLastError() here and it's unclear what errno gets set to if
6584 * the _wfopen() fails for missing wide functions. */
6585 }
6586
Bram Moolenaarf9e6c3b2014-11-05 18:36:03 +01006587 /* fopen() can open a file which name is longer than _MAX_PATH bytes
6588 * and shorter than _MAX_PATH characters successfully, but sometimes it
6589 * causes unexpected error in another part. We make it an error explicitly
6590 * here. */
6591 if (strlen(name) >= _MAX_PATH)
6592 return NULL;
6593
Bram Moolenaar071d4272004-06-13 20:20:40 +00006594 return fopen(name, mode);
6595}
6596#endif
6597
6598#ifdef FEAT_MBYTE
6599/*
6600 * SUB STREAM (aka info stream) handling:
6601 *
6602 * NTFS can have sub streams for each file. Normal contents of file is
6603 * stored in the main stream, and extra contents (author information and
6604 * title and so on) can be stored in sub stream. After Windows 2000, user
6605 * can access and store those informations in sub streams via explorer's
6606 * property menuitem in right click menu. Those informations in sub streams
6607 * were lost when copying only the main stream. So we have to copy sub
6608 * streams.
6609 *
6610 * Incomplete explanation:
6611 * http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp
6612 * More useful info and an example:
6613 * http://www.sysinternals.com/ntw2k/source/misc.shtml#streams
6614 */
6615
6616/*
6617 * Copy info stream data "substream". Read from the file with BackupRead(sh)
6618 * and write to stream "substream" of file "to".
6619 * Errors are ignored.
6620 */
6621 static void
6622copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len)
6623{
6624 HANDLE hTo;
6625 WCHAR *to_name;
6626
6627 to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR));
6628 wcscpy(to_name, to);
6629 wcscat(to_name, substream);
6630
6631 hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
6632 FILE_ATTRIBUTE_NORMAL, NULL);
6633 if (hTo != INVALID_HANDLE_VALUE)
6634 {
6635 long done;
6636 DWORD todo;
6637 DWORD readcnt, written;
6638 char buf[4096];
6639
6640 /* Copy block of bytes at a time. Abort when something goes wrong. */
6641 for (done = 0; done < len; done += written)
6642 {
6643 /* (size_t) cast for Borland C 5.5 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00006644 todo = (DWORD)((size_t)(len - done) > sizeof(buf) ? sizeof(buf)
6645 : (size_t)(len - done));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006646 if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt,
6647 FALSE, FALSE, context)
6648 || readcnt != todo
6649 || !WriteFile(hTo, buf, todo, &written, NULL)
6650 || written != todo)
6651 break;
6652 }
6653 CloseHandle(hTo);
6654 }
6655
6656 free(to_name);
6657}
6658
6659/*
6660 * Copy info streams from file "from" to file "to".
6661 */
6662 static void
6663copy_infostreams(char_u *from, char_u *to)
6664{
6665 WCHAR *fromw;
6666 WCHAR *tow;
6667 HANDLE sh;
6668 WIN32_STREAM_ID sid;
6669 int headersize;
6670 WCHAR streamname[_MAX_PATH];
6671 DWORD readcount;
6672 void *context = NULL;
6673 DWORD lo, hi;
6674 int len;
6675
6676 /* Convert the file names to wide characters. */
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006677 fromw = enc_to_utf16(from, NULL);
6678 tow = enc_to_utf16(to, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006679 if (fromw != NULL && tow != NULL)
6680 {
6681 /* Open the file for reading. */
6682 sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL,
6683 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
6684 if (sh != INVALID_HANDLE_VALUE)
6685 {
6686 /* Use BackupRead() to find the info streams. Repeat until we
6687 * have done them all.*/
6688 for (;;)
6689 {
6690 /* Get the header to find the length of the stream name. If
6691 * the "readcount" is zero we have done all info streams. */
6692 ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00006693 headersize = (int)((char *)&sid.cStreamName - (char *)&sid.dwStreamId);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006694 if (!BackupRead(sh, (LPBYTE)&sid, headersize,
6695 &readcount, FALSE, FALSE, &context)
6696 || readcount == 0)
6697 break;
6698
6699 /* We only deal with streams that have a name. The normal
6700 * file data appears to be without a name, even though docs
6701 * suggest it is called "::$DATA". */
6702 if (sid.dwStreamNameSize > 0)
6703 {
6704 /* Read the stream name. */
6705 if (!BackupRead(sh, (LPBYTE)streamname,
6706 sid.dwStreamNameSize,
6707 &readcount, FALSE, FALSE, &context))
6708 break;
6709
6710 /* Copy an info stream with a name ":anything:$DATA".
6711 * Skip "::$DATA", it has no stream name (examples suggest
6712 * it might be used for the normal file contents).
6713 * Note that BackupRead() counts bytes, but the name is in
6714 * wide characters. */
6715 len = readcount / sizeof(WCHAR);
6716 streamname[len] = 0;
6717 if (len > 7 && wcsicmp(streamname + len - 6,
6718 L":$DATA") == 0)
6719 {
6720 streamname[len - 6] = 0;
6721 copy_substream(sh, &context, tow, streamname,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00006722 (long)sid.Size.u.LowPart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006723 }
6724 }
6725
6726 /* Advance to the next stream. We might try seeking too far,
6727 * but BackupSeek() doesn't skip over stream borders, thus
6728 * that's OK. */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00006729 (void)BackupSeek(sh, sid.Size.u.LowPart, sid.Size.u.HighPart,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006730 &lo, &hi, &context);
6731 }
6732
6733 /* Clear the context. */
6734 (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context);
6735
6736 CloseHandle(sh);
6737 }
6738 }
6739 vim_free(fromw);
6740 vim_free(tow);
6741}
6742#endif
6743
6744/*
6745 * Copy file attributes from file "from" to file "to".
6746 * For Windows NT and later we copy info streams.
6747 * Always returns zero, errors are ignored.
6748 */
6749 int
6750mch_copy_file_attribute(char_u *from, char_u *to)
6751{
6752#ifdef FEAT_MBYTE
6753 /* File streams only work on Windows NT and later. */
6754 PlatformId();
6755 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
6756 copy_infostreams(from, to);
6757#endif
6758 return 0;
6759}
6760
6761#if defined(MYRESETSTKOFLW) || defined(PROTO)
6762/*
6763 * Recreate a destroyed stack guard page in win32.
6764 * Written by Benjamin Peterson.
6765 */
6766
6767/* These magic numbers are from the MS header files */
6768#define MIN_STACK_WIN9X 17
6769#define MIN_STACK_WINNT 2
6770
6771/*
6772 * This function does the same thing as _resetstkoflw(), which is only
6773 * available in DevStudio .net and later.
6774 * Returns 0 for failure, 1 for success.
6775 */
6776 int
6777myresetstkoflw(void)
6778{
6779 BYTE *pStackPtr;
6780 BYTE *pGuardPage;
6781 BYTE *pStackBase;
6782 BYTE *pLowestPossiblePage;
6783 MEMORY_BASIC_INFORMATION mbi;
6784 SYSTEM_INFO si;
6785 DWORD nPageSize;
6786 DWORD dummy;
6787
6788 /* This code will not work on win32s. */
6789 PlatformId();
6790 if (g_PlatformId == VER_PLATFORM_WIN32s)
6791 return 0;
6792
6793 /* We need to know the system page size. */
6794 GetSystemInfo(&si);
6795 nPageSize = si.dwPageSize;
6796
6797 /* ...and the current stack pointer */
6798 pStackPtr = (BYTE*)_alloca(1);
6799
6800 /* ...and the base of the stack. */
6801 if (VirtualQuery(pStackPtr, &mbi, sizeof mbi) == 0)
6802 return 0;
6803 pStackBase = (BYTE*)mbi.AllocationBase;
6804
6805 /* ...and the page thats min_stack_req pages away from stack base; this is
6806 * the lowest page we could use. */
6807 pLowestPossiblePage = pStackBase + ((g_PlatformId == VER_PLATFORM_WIN32_NT)
6808 ? MIN_STACK_WINNT : MIN_STACK_WIN9X) * nPageSize;
6809
6810 /* On Win95, we want the next page down from the end of the stack. */
6811 if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS)
6812 {
6813 /* Find the page that's only 1 page down from the page that the stack
6814 * ptr is in. */
6815 pGuardPage = (BYTE*)((DWORD)nPageSize * (((DWORD)pStackPtr
6816 / (DWORD)nPageSize) - 1));
6817 if (pGuardPage < pLowestPossiblePage)
6818 return 0;
6819
6820 /* Apply the noaccess attribute to the page -- there's no guard
6821 * attribute in win95-type OSes. */
6822 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_NOACCESS, &dummy))
6823 return 0;
6824 }
6825 else
6826 {
6827 /* On NT, however, we want the first committed page in the stack Start
6828 * at the stack base and move forward through memory until we find a
6829 * committed block. */
6830 BYTE *pBlock = pStackBase;
6831
Bram Moolenaara466c992005-07-09 21:03:22 +00006832 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006833 {
6834 if (VirtualQuery(pBlock, &mbi, sizeof mbi) == 0)
6835 return 0;
6836
6837 pBlock += mbi.RegionSize;
6838
6839 if (mbi.State & MEM_COMMIT)
6840 break;
6841 }
6842
6843 /* mbi now describes the first committed block in the stack. */
6844 if (mbi.Protect & PAGE_GUARD)
6845 return 1;
6846
6847 /* decide where the guard page should start */
6848 if ((long_u)(mbi.BaseAddress) < (long_u)pLowestPossiblePage)
6849 pGuardPage = pLowestPossiblePage;
6850 else
6851 pGuardPage = (BYTE*)mbi.BaseAddress;
6852
6853 /* allocate the guard page */
6854 if (!VirtualAlloc(pGuardPage, nPageSize, MEM_COMMIT, PAGE_READWRITE))
6855 return 0;
6856
6857 /* apply the guard attribute to the page */
6858 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_READWRITE | PAGE_GUARD,
6859 &dummy))
6860 return 0;
6861 }
6862
6863 return 1;
6864}
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006865#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006866
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006867
6868#if defined(FEAT_MBYTE) || defined(PROTO)
6869/*
6870 * The command line arguments in UCS2
6871 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00006872static int nArgsW = 0;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006873static LPWSTR *ArglistW = NULL;
6874static int global_argc = 0;
6875static char **global_argv;
6876
6877static int used_file_argc = 0; /* last argument in global_argv[] used
6878 for the argument list. */
6879static int *used_file_indexes = NULL; /* indexes in global_argv[] for
6880 command line arguments added to
6881 the argument list */
6882static int used_file_count = 0; /* nr of entries in used_file_indexes */
6883static int used_file_literal = FALSE; /* take file names literally */
6884static int used_file_full_path = FALSE; /* file name was full path */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006885static int used_file_diff_mode = FALSE; /* file name was with diff mode */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006886static int used_alist_count = 0;
6887
6888
6889/*
6890 * Get the command line arguments. Unicode version.
6891 * Returns argc. Zero when something fails.
6892 */
6893 int
6894get_cmd_argsW(char ***argvp)
6895{
6896 char **argv = NULL;
6897 int argc = 0;
6898 int i;
6899
Bram Moolenaar14993322014-09-09 12:25:33 +02006900 free_cmd_argsW();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006901 ArglistW = CommandLineToArgvW(GetCommandLineW(), &nArgsW);
6902 if (ArglistW != NULL)
6903 {
6904 argv = malloc((nArgsW + 1) * sizeof(char *));
6905 if (argv != NULL)
6906 {
6907 argc = nArgsW;
6908 argv[argc] = NULL;
6909 for (i = 0; i < argc; ++i)
6910 {
6911 int len;
6912
6913 /* Convert each Unicode argument to the current codepage. */
6914 WideCharToMultiByte_alloc(GetACP(), 0,
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00006915 ArglistW[i], (int)wcslen(ArglistW[i]) + 1,
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006916 (LPSTR *)&argv[i], &len, 0, 0);
6917 if (argv[i] == NULL)
6918 {
6919 /* Out of memory, clear everything. */
6920 while (i > 0)
6921 free(argv[--i]);
6922 free(argv);
Bram Moolenaar73c61632013-12-07 14:48:10 +01006923 argv = NULL;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006924 argc = 0;
6925 }
6926 }
6927 }
6928 }
6929
6930 global_argc = argc;
6931 global_argv = argv;
6932 if (argc > 0)
Bram Moolenaar14993322014-09-09 12:25:33 +02006933 {
6934 if (used_file_indexes != NULL)
6935 free(used_file_indexes);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006936 used_file_indexes = malloc(argc * sizeof(int));
Bram Moolenaar14993322014-09-09 12:25:33 +02006937 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006938
6939 if (argvp != NULL)
6940 *argvp = argv;
6941 return argc;
6942}
6943
6944 void
6945free_cmd_argsW(void)
6946{
6947 if (ArglistW != NULL)
6948 {
6949 GlobalFree(ArglistW);
6950 ArglistW = NULL;
6951 }
6952}
6953
6954/*
6955 * Remember "name" is an argument that was added to the argument list.
6956 * This avoids that we have to re-parse the argument list when fix_arg_enc()
6957 * is called.
6958 */
6959 void
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006960used_file_arg(char *name, int literal, int full_path, int diff_mode)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006961{
6962 int i;
6963
6964 if (used_file_indexes == NULL)
6965 return;
6966 for (i = used_file_argc + 1; i < global_argc; ++i)
6967 if (STRCMP(global_argv[i], name) == 0)
6968 {
6969 used_file_argc = i;
6970 used_file_indexes[used_file_count++] = i;
6971 break;
6972 }
6973 used_file_literal = literal;
6974 used_file_full_path = full_path;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006975 used_file_diff_mode = diff_mode;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006976}
6977
6978/*
6979 * Remember the length of the argument list as it was. If it changes then we
6980 * leave it alone when 'encoding' is set.
6981 */
6982 void
6983set_alist_count(void)
6984{
6985 used_alist_count = GARGCOUNT;
6986}
6987
6988/*
6989 * Fix the encoding of the command line arguments. Invoked when 'encoding'
6990 * has been changed while starting up. Use the UCS-2 command line arguments
6991 * and convert them to 'encoding'.
6992 */
6993 void
6994fix_arg_enc(void)
6995{
6996 int i;
6997 int idx;
6998 char_u *str;
Bram Moolenaar86b68352004-12-27 21:59:20 +00006999 int *fnum_list;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007000
7001 /* Safety checks:
7002 * - if argument count differs between the wide and non-wide argument
7003 * list, something must be wrong.
7004 * - the file name arguments must have been located.
7005 * - the length of the argument list wasn't changed by the user.
7006 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00007007 if (global_argc != nArgsW
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007008 || ArglistW == NULL
7009 || used_file_indexes == NULL
7010 || used_file_count == 0
7011 || used_alist_count != GARGCOUNT)
7012 return;
7013
Bram Moolenaar86b68352004-12-27 21:59:20 +00007014 /* Remember the buffer numbers for the arguments. */
7015 fnum_list = (int *)alloc((int)sizeof(int) * GARGCOUNT);
7016 if (fnum_list == NULL)
7017 return; /* out of memory */
7018 for (i = 0; i < GARGCOUNT; ++i)
7019 fnum_list[i] = GARGLIST[i].ae_fnum;
7020
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007021 /* Clear the argument list. Make room for the new arguments. */
7022 alist_clear(&global_alist);
7023 if (ga_grow(&global_alist.al_ga, used_file_count) == FAIL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00007024 return; /* out of memory */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007025
7026 for (i = 0; i < used_file_count; ++i)
7027 {
7028 idx = used_file_indexes[i];
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007029 str = utf16_to_enc(ArglistW[idx], NULL);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007030 if (str != NULL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00007031 {
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007032#ifdef FEAT_DIFF
7033 /* When using diff mode may need to concatenate file name to
7034 * directory name. Just like it's done in main(). */
7035 if (used_file_diff_mode && mch_isdir(str) && GARGCOUNT > 0
7036 && !mch_isdir(alist_name(&GARGLIST[0])))
7037 {
7038 char_u *r;
7039
7040 r = concat_fnames(str, gettail(alist_name(&GARGLIST[0])), TRUE);
7041 if (r != NULL)
7042 {
7043 vim_free(str);
7044 str = r;
7045 }
7046 }
7047#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00007048 /* Re-use the old buffer by renaming it. When not using literal
7049 * names it's done by alist_expand() below. */
7050 if (used_file_literal)
7051 buf_set_name(fnum_list[i], str);
7052
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007053 alist_add(&global_alist, str, used_file_literal ? 2 : 0);
Bram Moolenaar86b68352004-12-27 21:59:20 +00007054 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007055 }
7056
7057 if (!used_file_literal)
7058 {
7059 /* Now expand wildcards in the arguments. */
7060 /* Temporarily add '(' and ')' to 'isfname'. These are valid
7061 * filename characters but are excluded from 'isfname' to make
7062 * "gf" work on a file name in parenthesis (e.g.: see vim.h). */
7063 do_cmdline_cmd((char_u *)":let SaVe_ISF = &isf|set isf+=(,)");
Bram Moolenaar86b68352004-12-27 21:59:20 +00007064 alist_expand(fnum_list, used_alist_count);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007065 do_cmdline_cmd((char_u *)":let &isf = SaVe_ISF|unlet SaVe_ISF");
7066 }
7067
7068 /* If wildcard expansion failed, we are editing the first file of the
7069 * arglist and there is no file name: Edit the first argument now. */
7070 if (curwin->w_arg_idx == 0 && curbuf->b_fname == NULL)
7071 {
7072 do_cmdline_cmd((char_u *)":rewind");
7073 if (GARGCOUNT == 1 && used_file_full_path)
7074 (void)vim_chdirfile(alist_name(&GARGLIST[0]));
7075 }
Bram Moolenaar86b68352004-12-27 21:59:20 +00007076
7077 set_alist_count();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007078}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007079#endif