blob: adbb199b9dfb45eafd0828e450e599b1066d8596 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9/*
10 * os_win32.c
11 *
12 * Used for both the console version and the Win32 GUI. A lot of code is for
13 * the console version only, so there is a lot of "#ifndef FEAT_GUI_W32".
14 *
15 * Win32 (Windows NT and Windows 95) system-dependent routines.
16 * Portions lifted from the Win32 SDK samples, the MSDOS-dependent code,
17 * NetHack 3.1.3, GNU Emacs 19.30, and Vile 5.5.
18 *
19 * George V. Reilly <george@reilly.org> wrote most of this.
20 * Roger Knobbe <rogerk@wonderware.com> did the initial port of Vim 3.0.
21 */
22
Bram Moolenaar071d4272004-06-13 20:20:40 +000023#include "vim.h"
24
Bram Moolenaar325b7a22004-07-05 15:58:32 +000025#ifdef FEAT_MZSCHEME
26# include "if_mzsch.h"
27#endif
28
Bram Moolenaar071d4272004-06-13 20:20:40 +000029#include <sys/types.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000030#include <signal.h>
31#include <limits.h>
Bram Moolenaar82881492012-11-20 16:53:39 +010032
33/* cproto fails on missing include files */
34#ifndef PROTO
35# include <process.h>
36#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000037
38#undef chdir
39#ifdef __GNUC__
40# ifndef __MINGW32__
41# include <dirent.h>
42# endif
43#else
44# include <direct.h>
45#endif
46
Bram Moolenaar82881492012-11-20 16:53:39 +010047#ifndef PROTO
48# if defined(FEAT_TITLE) && !defined(FEAT_GUI_W32)
49# include <shellapi.h>
50# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000051#endif
52
Bram Moolenaarfb630902016-10-29 14:55:00 +020053#ifdef FEAT_JOB_CHANNEL
54# include <tlhelp32.h>
55#endif
56
Bram Moolenaar071d4272004-06-13 20:20:40 +000057#ifdef __MINGW32__
58# ifndef FROM_LEFT_1ST_BUTTON_PRESSED
59# define FROM_LEFT_1ST_BUTTON_PRESSED 0x0001
60# endif
61# ifndef RIGHTMOST_BUTTON_PRESSED
62# define RIGHTMOST_BUTTON_PRESSED 0x0002
63# endif
64# ifndef FROM_LEFT_2ND_BUTTON_PRESSED
65# define FROM_LEFT_2ND_BUTTON_PRESSED 0x0004
66# endif
67# ifndef FROM_LEFT_3RD_BUTTON_PRESSED
68# define FROM_LEFT_3RD_BUTTON_PRESSED 0x0008
69# endif
70# ifndef FROM_LEFT_4TH_BUTTON_PRESSED
71# define FROM_LEFT_4TH_BUTTON_PRESSED 0x0010
72# endif
73
74/*
75 * EventFlags
76 */
77# ifndef MOUSE_MOVED
78# define MOUSE_MOVED 0x0001
79# endif
80# ifndef DOUBLE_CLICK
81# define DOUBLE_CLICK 0x0002
82# endif
83#endif
84
85/* Record all output and all keyboard & mouse input */
86/* #define MCH_WRITE_DUMP */
87
88#ifdef MCH_WRITE_DUMP
89FILE* fdDump = NULL;
90#endif
91
92/*
93 * When generating prototypes for Win32 on Unix, these lines make the syntax
94 * errors disappear. They do not need to be correct.
95 */
96#ifdef PROTO
97#define WINAPI
Bram Moolenaar071d4272004-06-13 20:20:40 +000098typedef char * LPCSTR;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +000099typedef char * LPWSTR;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000100typedef int ACCESS_MASK;
101typedef int BOOL;
102typedef int COLORREF;
103typedef int CONSOLE_CURSOR_INFO;
104typedef int COORD;
105typedef int DWORD;
106typedef int HANDLE;
Bram Moolenaaref269542016-01-19 13:22:12 +0100107typedef int LPHANDLE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000108typedef int HDC;
109typedef int HFONT;
110typedef int HICON;
111typedef int HINSTANCE;
112typedef int HWND;
113typedef int INPUT_RECORD;
114typedef int KEY_EVENT_RECORD;
115typedef int LOGFONT;
116typedef int LPBOOL;
117typedef int LPCTSTR;
118typedef int LPDWORD;
119typedef int LPSTR;
120typedef int LPTSTR;
121typedef int LPVOID;
122typedef int MOUSE_EVENT_RECORD;
123typedef int PACL;
124typedef int PDWORD;
125typedef int PHANDLE;
126typedef int PRINTDLG;
127typedef int PSECURITY_DESCRIPTOR;
128typedef int PSID;
129typedef int SECURITY_INFORMATION;
130typedef int SHORT;
131typedef int SMALL_RECT;
132typedef int TEXTMETRIC;
133typedef int TOKEN_INFORMATION_CLASS;
134typedef int TRUSTEE;
135typedef int WORD;
136typedef int WCHAR;
137typedef void VOID;
Bram Moolenaar82881492012-11-20 16:53:39 +0100138typedef int BY_HANDLE_FILE_INFORMATION;
Bram Moolenaar32ac8cd2013-07-03 18:49:17 +0200139typedef int SE_OBJECT_TYPE;
140typedef int PSNSECINFO;
141typedef int PSNSECINFOW;
Bram Moolenaarb8e0bdb2014-11-12 16:10:48 +0100142typedef int STARTUPINFO;
143typedef int PROCESS_INFORMATION;
Bram Moolenaard90b6c02016-08-28 18:10:45 +0200144typedef int LPSECURITY_ATTRIBUTES;
145# define __stdcall /* empty */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000146#endif
147
Bram Moolenaar071d4272004-06-13 20:20:40 +0000148#if defined(__BORLANDC__)
149/* Strangely Borland uses a non-standard name. */
150# define wcsicmp(a, b) wcscmpi((a), (b))
151#endif
152
153#ifndef FEAT_GUI_W32
154/* Win32 Console handles for input and output */
155static HANDLE g_hConIn = INVALID_HANDLE_VALUE;
156static HANDLE g_hConOut = INVALID_HANDLE_VALUE;
157
158/* Win32 Screen buffer,coordinate,console I/O information */
159static SMALL_RECT g_srScrollRegion;
160static COORD g_coord; /* 0-based, but external coords are 1-based */
161
162/* The attribute of the screen when the editor was started */
163static WORD g_attrDefault = 7; /* lightgray text on black background */
164static WORD g_attrCurrent;
165
166static int g_fCBrkPressed = FALSE; /* set by ctrl-break interrupt */
167static int g_fCtrlCPressed = FALSE; /* set when ctrl-C or ctrl-break detected */
168static int g_fForceExit = FALSE; /* set when forcefully exiting */
169
170static void termcap_mode_start(void);
171static void termcap_mode_end(void);
172static void clear_chars(COORD coord, DWORD n);
173static void clear_screen(void);
174static void clear_to_end_of_display(void);
175static void clear_to_end_of_line(void);
176static void scroll(unsigned cLines);
177static void set_scroll_region(unsigned left, unsigned top,
178 unsigned right, unsigned bottom);
179static void insert_lines(unsigned cLines);
180static void delete_lines(unsigned cLines);
181static void gotoxy(unsigned x, unsigned y);
182static void normvideo(void);
183static void textattr(WORD wAttr);
184static void textcolor(WORD wAttr);
185static void textbackground(WORD wAttr);
186static void standout(void);
187static void standend(void);
188static void visual_bell(void);
189static void cursor_visible(BOOL fVisible);
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200190static DWORD write_chars(char_u *pchBuf, DWORD cbToWrite);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000191static void create_conin(void);
192static int s_cursor_visible = TRUE;
193static int did_create_conin = FALSE;
194#else
195static int s_dont_use_vimrun = TRUE;
196static int need_vimrun_warning = FALSE;
197static char *vimrun_path = "vimrun ";
198#endif
199
Bram Moolenaar12b559e2013-06-12 22:41:37 +0200200static int win32_getattrs(char_u *name);
201static int win32_setattrs(char_u *name, int attrs);
202static int win32_set_archive(char_u *name);
203
Bram Moolenaar071d4272004-06-13 20:20:40 +0000204#ifndef FEAT_GUI_W32
205static int suppress_winsize = 1; /* don't fiddle with console */
206#endif
207
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200208static char_u *exe_path = NULL;
209
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100210static BOOL win8_or_later = FALSE;
211
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100212#ifndef FEAT_GUI_W32
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100213/*
214 * Version of ReadConsoleInput() that works with IME.
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100215 * Works around problems on Windows 8.
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100216 */
217 static BOOL
218read_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100219 HANDLE hInput,
220 INPUT_RECORD *lpBuffer,
221 DWORD nLength,
222 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100223{
224 enum
225 {
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100226 IRSIZE = 10
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100227 };
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100228 static INPUT_RECORD s_irCache[IRSIZE];
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100229 static DWORD s_dwIndex = 0;
230 static DWORD s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100231 DWORD dwEvents;
Bram Moolenaardd415a62014-02-05 14:02:27 +0100232 int head;
233 int tail;
234 int i;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100235
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200236 if (nLength == -2)
237 return (s_dwMax > 0) ? TRUE : FALSE;
238
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100239 if (!win8_or_later)
240 {
241 if (nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200242 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
243 return ReadConsoleInputW(hInput, lpBuffer, 1, &dwEvents);
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100244 }
245
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100246 if (s_dwMax == 0)
247 {
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100248 if (nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200249 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
250 if (!ReadConsoleInputW(hInput, s_irCache, IRSIZE, &dwEvents))
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100251 return FALSE;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100252 s_dwIndex = 0;
253 s_dwMax = dwEvents;
254 if (dwEvents == 0)
255 {
256 *lpEvents = 0;
257 return TRUE;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100258 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100259
260 if (s_dwMax > 1)
261 {
262 head = 0;
263 tail = s_dwMax - 1;
264 while (head != tail)
265 {
266 if (s_irCache[head].EventType == WINDOW_BUFFER_SIZE_EVENT
267 && s_irCache[head + 1].EventType
268 == WINDOW_BUFFER_SIZE_EVENT)
269 {
270 /* Remove duplicate event to avoid flicker. */
271 for (i = head; i < tail; ++i)
272 s_irCache[i] = s_irCache[i + 1];
273 --tail;
274 continue;
275 }
276 head++;
277 }
278 s_dwMax = tail + 1;
279 }
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100280 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100281
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100282 *lpBuffer = s_irCache[s_dwIndex];
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200283 if (!(nLength == -1 || nLength == -2) && ++s_dwIndex >= s_dwMax)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100284 s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100285 *lpEvents = 1;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100286 return TRUE;
287}
288
289/*
290 * Version of PeekConsoleInput() that works with IME.
291 */
292 static BOOL
293peek_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100294 HANDLE hInput,
295 INPUT_RECORD *lpBuffer,
296 DWORD nLength,
297 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100298{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100299 return read_console_input(hInput, lpBuffer, -1, lpEvents);
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100300}
301
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100302# ifdef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200303 static DWORD
304msg_wait_for_multiple_objects(
305 DWORD nCount,
306 LPHANDLE pHandles,
307 BOOL fWaitAll,
308 DWORD dwMilliseconds,
309 DWORD dwWakeMask)
310{
311 if (read_console_input(NULL, NULL, -2, NULL))
312 return WAIT_OBJECT_0;
313 return MsgWaitForMultipleObjects(nCount, pHandles, fWaitAll,
314 dwMilliseconds, dwWakeMask);
315}
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100316# endif
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200317
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100318# ifndef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200319 static DWORD
320wait_for_single_object(
321 HANDLE hHandle,
322 DWORD dwMilliseconds)
323{
324 if (read_console_input(NULL, NULL, -2, NULL))
325 return WAIT_OBJECT_0;
326 return WaitForSingleObject(hHandle, dwMilliseconds);
327}
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100328# endif
329#endif
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200330
Bram Moolenaar071d4272004-06-13 20:20:40 +0000331 static void
332get_exe_name(void)
333{
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100334 /* Maximum length of $PATH is more than MAXPATHL. 8191 is often mentioned
335 * as the maximum length that works (plus a NUL byte). */
336#define MAX_ENV_PATH_LEN 8192
337 char temp[MAX_ENV_PATH_LEN];
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200338 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000339
340 if (exe_name == NULL)
341 {
342 /* store the name of the executable, may be used for $VIM */
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100343 GetModuleFileName(NULL, temp, MAX_ENV_PATH_LEN - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000344 if (*temp != NUL)
345 exe_name = FullName_save((char_u *)temp, FALSE);
346 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000347
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200348 if (exe_path == NULL && exe_name != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000349 {
Bram Moolenaar6b5ef062010-10-27 12:18:00 +0200350 exe_path = vim_strnsave(exe_name,
351 (int)(gettail_sep(exe_name) - exe_name));
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200352 if (exe_path != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000353 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200354 /* Append our starting directory to $PATH, so that when doing
355 * "!xxd" it's found in our starting directory. Needed because
356 * SearchPath() also looks there. */
357 p = mch_getenv("PATH");
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100358 if (p == NULL
359 || STRLEN(p) + STRLEN(exe_path) + 2 < MAX_ENV_PATH_LEN)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200360 {
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100361 if (p == NULL || *p == NUL)
362 temp[0] = NUL;
363 else
364 {
365 STRCPY(temp, p);
366 STRCAT(temp, ";");
367 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200368 STRCAT(temp, exe_path);
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100369 vim_setenv((char_u *)"PATH", (char_u *)temp);
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200370 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000371 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000372 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000373}
374
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200375/*
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100376 * Unescape characters in "p" that appear in "escaped".
377 */
378 static void
379unescape_shellxquote(char_u *p, char_u *escaped)
380{
Bram Moolenaar4336cdf2012-02-29 13:58:47 +0100381 int l = (int)STRLEN(p);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100382 int n;
383
384 while (*p != NUL)
385 {
386 if (*p == '^' && vim_strchr(escaped, p[1]) != NULL)
387 mch_memmove(p, p + 1, l--);
388#ifdef FEAT_MBYTE
389 n = (*mb_ptr2len)(p);
390#else
391 n = 1;
392#endif
393 p += n;
394 l -= n;
395 }
396}
397
398/*
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200399 * Load library "name".
400 */
401 HINSTANCE
402vimLoadLib(char *name)
403{
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200404 HINSTANCE dll = NULL;
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200405
Bram Moolenaarfaca8402012-10-21 02:37:10 +0200406 /* NOTE: Do not use mch_dirname() and mch_chdir() here, they may call
407 * vimLoadLib() recursively, which causes a stack overflow. */
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200408 if (exe_path == NULL)
409 get_exe_name();
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200410 if (exe_path != NULL)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200411 {
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200412 WCHAR old_dirw[MAXPATHL];
413
414 if (GetCurrentDirectoryW(MAXPATHL, old_dirw) != 0)
415 {
416 /* Change directory to where the executable is, both to make
417 * sure we find a .dll there and to avoid looking for a .dll
418 * in the current directory. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100419 SetCurrentDirectory((LPCSTR)exe_path);
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200420 dll = LoadLibrary(name);
421 SetCurrentDirectoryW(old_dirw);
422 return dll;
423 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200424 }
425 return dll;
426}
427
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100428#if defined(DYNAMIC_ICONV) || defined(DYNAMIC_GETTEXT) || defined(PROTO)
429/*
430 * Get related information about 'funcname' which is imported by 'hInst'.
431 * If 'info' is 0, return the function address.
432 * If 'info' is 1, return the module name which the function is imported from.
433 */
434 static void *
435get_imported_func_info(HINSTANCE hInst, const char *funcname, int info)
436{
437 PBYTE pImage = (PBYTE)hInst;
438 PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)hInst;
439 PIMAGE_NT_HEADERS pPE;
440 PIMAGE_IMPORT_DESCRIPTOR pImpDesc;
441 PIMAGE_THUNK_DATA pIAT; /* Import Address Table */
442 PIMAGE_THUNK_DATA pINT; /* Import Name Table */
443 PIMAGE_IMPORT_BY_NAME pImpName;
444
445 if (pDOS->e_magic != IMAGE_DOS_SIGNATURE)
446 return NULL;
447 pPE = (PIMAGE_NT_HEADERS)(pImage + pDOS->e_lfanew);
448 if (pPE->Signature != IMAGE_NT_SIGNATURE)
449 return NULL;
450 pImpDesc = (PIMAGE_IMPORT_DESCRIPTOR)(pImage
451 + pPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
452 .VirtualAddress);
453 for (; pImpDesc->FirstThunk; ++pImpDesc)
454 {
455 if (!pImpDesc->OriginalFirstThunk)
456 continue;
457 pIAT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->FirstThunk);
458 pINT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->OriginalFirstThunk);
459 for (; pIAT->u1.Function; ++pIAT, ++pINT)
460 {
461 if (IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal))
462 continue;
463 pImpName = (PIMAGE_IMPORT_BY_NAME)(pImage
464 + (UINT_PTR)(pINT->u1.AddressOfData));
465 if (strcmp((char *)pImpName->Name, funcname) == 0)
466 {
467 switch (info)
468 {
469 case 0:
470 return (void *)pIAT->u1.Function;
471 case 1:
472 return (void *)(pImage + pImpDesc->Name);
473 default:
474 return NULL;
475 }
476 }
477 }
478 }
479 return NULL;
480}
481
482/*
483 * Get the module handle which 'funcname' in 'hInst' is imported from.
484 */
485 HINSTANCE
486find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname)
487{
488 char *modulename;
489
490 modulename = (char *)get_imported_func_info(hInst, funcname, 1);
491 if (modulename != NULL)
492 return GetModuleHandleA(modulename);
493 return NULL;
494}
495
496/*
497 * Get the address of 'funcname' which is imported by 'hInst' DLL.
498 */
499 void *
500get_dll_import_func(HINSTANCE hInst, const char *funcname)
501{
502 return get_imported_func_info(hInst, funcname, 0);
503}
504#endif
505
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506#if defined(DYNAMIC_GETTEXT) || defined(PROTO)
507# ifndef GETTEXT_DLL
508# define GETTEXT_DLL "libintl.dll"
Bram Moolenaar286eacd2016-01-16 18:05:50 +0100509# define GETTEXT_DLL_ALT "libintl-8.dll"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000510# endif
Bram Moolenaarf6a2b082012-06-29 13:14:03 +0200511/* Dummy functions */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000512static char *null_libintl_gettext(const char *);
Bram Moolenaaree695f72016-08-03 22:08:45 +0200513static char *null_libintl_ngettext(const char *, const char *, unsigned long n);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000514static char *null_libintl_textdomain(const char *);
515static char *null_libintl_bindtextdomain(const char *, const char *);
516static char *null_libintl_bind_textdomain_codeset(const char *, const char *);
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100517static int null_libintl_putenv(const char *);
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100518static int null_libintl_wputenv(const wchar_t *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200520static HINSTANCE hLibintlDLL = NULL;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000521char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
Bram Moolenaaree695f72016-08-03 22:08:45 +0200522char *(*dyn_libintl_ngettext)(const char *, const char *, unsigned long n)
523 = null_libintl_ngettext;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000524char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
525char *(*dyn_libintl_bindtextdomain)(const char *, const char *)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000526 = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000527char *(*dyn_libintl_bind_textdomain_codeset)(const char *, const char *)
528 = null_libintl_bind_textdomain_codeset;
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100529int (*dyn_libintl_putenv)(const char *) = null_libintl_putenv;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100530int (*dyn_libintl_wputenv)(const wchar_t *) = null_libintl_wputenv;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000531
532 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100533dyn_libintl_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000534{
535 int i;
536 static struct
537 {
538 char *name;
539 FARPROC *ptr;
540 } libintl_entry[] =
541 {
542 {"gettext", (FARPROC*)&dyn_libintl_gettext},
Bram Moolenaaree695f72016-08-03 22:08:45 +0200543 {"ngettext", (FARPROC*)&dyn_libintl_ngettext},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000544 {"textdomain", (FARPROC*)&dyn_libintl_textdomain},
545 {"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
546 {NULL, NULL}
547 };
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100548 HINSTANCE hmsvcrt;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000549
550 /* No need to initialize twice. */
551 if (hLibintlDLL)
552 return 1;
553 /* Load gettext library (libintl.dll) */
Bram Moolenaar923e43b2016-01-28 15:07:38 +0100554 hLibintlDLL = vimLoadLib(GETTEXT_DLL);
Bram Moolenaar938ee832016-01-24 15:36:03 +0100555#ifdef GETTEXT_DLL_ALT
Bram Moolenaar286eacd2016-01-16 18:05:50 +0100556 if (!hLibintlDLL)
557 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT);
Bram Moolenaar938ee832016-01-24 15:36:03 +0100558#endif
559 if (!hLibintlDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000560 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200561 if (p_verbose > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000562 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200563 verbose_enter();
564 EMSG2(_(e_loadlib), GETTEXT_DLL);
565 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200567 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000568 }
569 for (i = 0; libintl_entry[i].name != NULL
570 && libintl_entry[i].ptr != NULL; ++i)
571 {
572 if ((*libintl_entry[i].ptr = (FARPROC)GetProcAddress(hLibintlDLL,
573 libintl_entry[i].name)) == NULL)
574 {
575 dyn_libintl_end();
576 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000577 {
578 verbose_enter();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000579 EMSG2(_(e_loadfunc), libintl_entry[i].name);
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000580 verbose_leave();
581 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000582 return 0;
583 }
584 }
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000585
586 /* The bind_textdomain_codeset() function is optional. */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000587 dyn_libintl_bind_textdomain_codeset = (void *)GetProcAddress(hLibintlDLL,
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000588 "bind_textdomain_codeset");
589 if (dyn_libintl_bind_textdomain_codeset == NULL)
590 dyn_libintl_bind_textdomain_codeset =
591 null_libintl_bind_textdomain_codeset;
592
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100593 /* _putenv() function for the libintl.dll is optional. */
594 hmsvcrt = find_imported_module_by_funcname(hLibintlDLL, "getenv");
595 if (hmsvcrt != NULL)
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100596 {
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100597 dyn_libintl_putenv = (void *)GetProcAddress(hmsvcrt, "_putenv");
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100598 dyn_libintl_wputenv = (void *)GetProcAddress(hmsvcrt, "_wputenv");
599 }
600 if (dyn_libintl_putenv == NULL || dyn_libintl_putenv == _putenv)
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100601 dyn_libintl_putenv = null_libintl_putenv;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100602 if (dyn_libintl_wputenv == NULL || dyn_libintl_wputenv == _wputenv)
603 dyn_libintl_wputenv = null_libintl_wputenv;
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100604
Bram Moolenaar071d4272004-06-13 20:20:40 +0000605 return 1;
606}
607
608 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100609dyn_libintl_end(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000610{
611 if (hLibintlDLL)
612 FreeLibrary(hLibintlDLL);
613 hLibintlDLL = NULL;
614 dyn_libintl_gettext = null_libintl_gettext;
Bram Moolenaaree695f72016-08-03 22:08:45 +0200615 dyn_libintl_ngettext = null_libintl_ngettext;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000616 dyn_libintl_textdomain = null_libintl_textdomain;
617 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000618 dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset;
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100619 dyn_libintl_putenv = null_libintl_putenv;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100620 dyn_libintl_wputenv = null_libintl_wputenv;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621}
622
623 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000624null_libintl_gettext(const char *msgid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625{
626 return (char*)msgid;
627}
628
629 static char *
Bram Moolenaaree695f72016-08-03 22:08:45 +0200630null_libintl_ngettext(
631 const char *msgid,
632 const char *msgid_plural,
633 unsigned long n)
634{
Bram Moolenaarc90f2ae2016-08-04 22:00:15 +0200635 return (char *)(n == 1 ? msgid : msgid_plural);
Bram Moolenaaree695f72016-08-03 22:08:45 +0200636}
637
Bram Moolenaaree695f72016-08-03 22:08:45 +0200638 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100639null_libintl_bindtextdomain(
640 const char *domainname UNUSED,
641 const char *dirname UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000642{
643 return NULL;
644}
645
646 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100647null_libintl_bind_textdomain_codeset(
648 const char *domainname UNUSED,
649 const char *codeset UNUSED)
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000650{
651 return NULL;
652}
653
654 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100655null_libintl_textdomain(const char *domainname UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000656{
657 return NULL;
658}
659
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100660 int
Bram Moolenaar1266d672017-02-01 13:43:36 +0100661null_libintl_putenv(const char *envstring UNUSED)
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100662{
663 return 0;
664}
665
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100666 int
Bram Moolenaar1266d672017-02-01 13:43:36 +0100667null_libintl_wputenv(const wchar_t *envstring UNUSED)
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100668{
669 return 0;
670}
671
Bram Moolenaar071d4272004-06-13 20:20:40 +0000672#endif /* DYNAMIC_GETTEXT */
673
674/* This symbol is not defined in older versions of the SDK or Visual C++ */
675
676#ifndef VER_PLATFORM_WIN32_WINDOWS
677# define VER_PLATFORM_WIN32_WINDOWS 1
678#endif
679
680DWORD g_PlatformId;
681
682#ifdef HAVE_ACL
Bram Moolenaar82881492012-11-20 16:53:39 +0100683# ifndef PROTO
684# include <aclapi.h>
685# endif
Bram Moolenaar27515922013-06-29 15:36:26 +0200686# ifndef PROTECTED_DACL_SECURITY_INFORMATION
687# define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000L
688# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689#endif
690
Bram Moolenaar27515922013-06-29 15:36:26 +0200691#ifdef HAVE_ACL
692/*
693 * Enables or disables the specified privilege.
694 */
695 static BOOL
696win32_enable_privilege(LPTSTR lpszPrivilege, BOOL bEnable)
697{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100698 BOOL bResult;
699 LUID luid;
700 HANDLE hToken;
701 TOKEN_PRIVILEGES tokenPrivileges;
Bram Moolenaar27515922013-06-29 15:36:26 +0200702
703 if (!OpenProcessToken(GetCurrentProcess(),
704 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
705 return FALSE;
706
707 if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
708 {
709 CloseHandle(hToken);
710 return FALSE;
711 }
712
Bram Moolenaar45500912014-07-09 20:51:07 +0200713 tokenPrivileges.PrivilegeCount = 1;
Bram Moolenaar27515922013-06-29 15:36:26 +0200714 tokenPrivileges.Privileges[0].Luid = luid;
715 tokenPrivileges.Privileges[0].Attributes = bEnable ?
716 SE_PRIVILEGE_ENABLED : 0;
717
718 bResult = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges,
719 sizeof(TOKEN_PRIVILEGES), NULL, NULL);
720
721 CloseHandle(hToken);
722
723 return bResult && GetLastError() == ERROR_SUCCESS;
724}
725#endif
726
Bram Moolenaar071d4272004-06-13 20:20:40 +0000727/*
728 * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
729 * VER_PLATFORM_WIN32_WINDOWS (Win95).
730 */
731 void
732PlatformId(void)
733{
734 static int done = FALSE;
735
736 if (!done)
737 {
738 OSVERSIONINFO ovi;
739
740 ovi.dwOSVersionInfoSize = sizeof(ovi);
741 GetVersionEx(&ovi);
742
743 g_PlatformId = ovi.dwPlatformId;
744
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100745 if ((ovi.dwMajorVersion == 6 && ovi.dwMinorVersion >= 2)
746 || ovi.dwMajorVersion > 6)
747 win8_or_later = TRUE;
748
Bram Moolenaar071d4272004-06-13 20:20:40 +0000749#ifdef HAVE_ACL
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200750 /* Enable privilege for getting or setting SACLs. */
751 win32_enable_privilege(SE_SECURITY_NAME, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000752#endif
753 done = TRUE;
754 }
755}
756
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757#ifndef FEAT_GUI_W32
758
759#define SHIFT (SHIFT_PRESSED)
760#define CTRL (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
761#define ALT (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
762#define ALT_GR (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)
763
764
765/* When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
766 * We map function keys to their ANSI terminal equivalents, as produced
767 * by ANSI.SYS, for compatibility with the MS-DOS version of Vim. Any
768 * ANSI key with a value >= '\300' is nonstandard, but provided anyway
769 * so that the user can have access to all SHIFT-, CTRL-, and ALT-
770 * combinations of function/arrow/etc keys.
771 */
772
Bram Moolenaard6f676d2005-06-01 21:51:55 +0000773static const struct
Bram Moolenaar071d4272004-06-13 20:20:40 +0000774{
775 WORD wVirtKey;
776 BOOL fAnsiKey;
777 int chAlone;
778 int chShift;
779 int chCtrl;
780 int chAlt;
781} VirtKeyMap[] =
782{
783
784/* Key ANSI alone shift ctrl alt */
785 { VK_ESCAPE,FALSE, ESC, ESC, ESC, ESC, },
786
787 { VK_F1, TRUE, ';', 'T', '^', 'h', },
788 { VK_F2, TRUE, '<', 'U', '_', 'i', },
789 { VK_F3, TRUE, '=', 'V', '`', 'j', },
790 { VK_F4, TRUE, '>', 'W', 'a', 'k', },
791 { VK_F5, TRUE, '?', 'X', 'b', 'l', },
792 { VK_F6, TRUE, '@', 'Y', 'c', 'm', },
793 { VK_F7, TRUE, 'A', 'Z', 'd', 'n', },
794 { VK_F8, TRUE, 'B', '[', 'e', 'o', },
795 { VK_F9, TRUE, 'C', '\\', 'f', 'p', },
796 { VK_F10, TRUE, 'D', ']', 'g', 'q', },
797 { VK_F11, TRUE, '\205', '\207', '\211', '\213', },
798 { VK_F12, TRUE, '\206', '\210', '\212', '\214', },
799
800 { VK_HOME, TRUE, 'G', '\302', 'w', '\303', },
801 { VK_UP, TRUE, 'H', '\304', '\305', '\306', },
802 { VK_PRIOR, TRUE, 'I', '\307', '\204', '\310', }, /*PgUp*/
803 { VK_LEFT, TRUE, 'K', '\311', 's', '\312', },
804 { VK_RIGHT, TRUE, 'M', '\313', 't', '\314', },
805 { VK_END, TRUE, 'O', '\315', 'u', '\316', },
806 { VK_DOWN, TRUE, 'P', '\317', '\320', '\321', },
807 { VK_NEXT, TRUE, 'Q', '\322', 'v', '\323', }, /*PgDn*/
808 { VK_INSERT,TRUE, 'R', '\324', '\325', '\326', },
809 { VK_DELETE,TRUE, 'S', '\327', '\330', '\331', },
810
811 { VK_SNAPSHOT,TRUE, 0, 0, 0, 'r', }, /*PrtScrn*/
812
813#if 0
814 /* Most people don't have F13-F20, but what the hell... */
815 { VK_F13, TRUE, '\332', '\333', '\334', '\335', },
816 { VK_F14, TRUE, '\336', '\337', '\340', '\341', },
817 { VK_F15, TRUE, '\342', '\343', '\344', '\345', },
818 { VK_F16, TRUE, '\346', '\347', '\350', '\351', },
819 { VK_F17, TRUE, '\352', '\353', '\354', '\355', },
820 { VK_F18, TRUE, '\356', '\357', '\360', '\361', },
821 { VK_F19, TRUE, '\362', '\363', '\364', '\365', },
822 { VK_F20, TRUE, '\366', '\367', '\370', '\371', },
823#endif
824 { VK_ADD, TRUE, 'N', 'N', 'N', 'N', }, /* keyp '+' */
825 { VK_SUBTRACT, TRUE,'J', 'J', 'J', 'J', }, /* keyp '-' */
826 /* { VK_DIVIDE, TRUE,'N', 'N', 'N', 'N', }, keyp '/' */
827 { VK_MULTIPLY, TRUE,'7', '7', '7', '7', }, /* keyp '*' */
828
829 { VK_NUMPAD0,TRUE, '\332', '\333', '\334', '\335', },
830 { VK_NUMPAD1,TRUE, '\336', '\337', '\340', '\341', },
831 { VK_NUMPAD2,TRUE, '\342', '\343', '\344', '\345', },
832 { VK_NUMPAD3,TRUE, '\346', '\347', '\350', '\351', },
833 { VK_NUMPAD4,TRUE, '\352', '\353', '\354', '\355', },
834 { VK_NUMPAD5,TRUE, '\356', '\357', '\360', '\361', },
835 { VK_NUMPAD6,TRUE, '\362', '\363', '\364', '\365', },
836 { VK_NUMPAD7,TRUE, '\366', '\367', '\370', '\371', },
837 { VK_NUMPAD8,TRUE, '\372', '\373', '\374', '\375', },
838 /* Sorry, out of number space! <negri>*/
839 { VK_NUMPAD9,TRUE, '\376', '\377', '\377', '\367', },
840
841};
842
843
844#ifdef _MSC_VER
845// The ToAscii bug destroys several registers. Need to turn off optimization
846// or the GetConsoleKeyboardLayoutName hack will fail in non-debug versions
Bram Moolenaar7b5f8322006-03-23 22:47:08 +0000847# pragma warning(push)
848# pragma warning(disable: 4748)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000849# pragma optimize("", off)
850#endif
851
852#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200853# define UChar UnicodeChar
Bram Moolenaar071d4272004-06-13 20:20:40 +0000854#else
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200855# define UChar uChar.UnicodeChar
Bram Moolenaar071d4272004-06-13 20:20:40 +0000856#endif
857
858/* The return code indicates key code size. */
859 static int
860#ifdef __BORLANDC__
861 __stdcall
862#endif
863win32_kbd_patch_key(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000864 KEY_EVENT_RECORD *pker)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000865{
866 UINT uMods = pker->dwControlKeyState;
867 static int s_iIsDead = 0;
868 static WORD awAnsiCode[2];
869 static BYTE abKeystate[256];
870
871
872 if (s_iIsDead == 2)
873 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200874 pker->UChar = (WCHAR) awAnsiCode[1];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000875 s_iIsDead = 0;
876 return 1;
877 }
878
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200879 if (pker->UChar != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000880 return 1;
881
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200882 vim_memset(abKeystate, 0, sizeof (abKeystate));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000883
Bram Moolenaar071d4272004-06-13 20:20:40 +0000884 /* Clear any pending dead keys */
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200885 ToUnicode(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000886
887 if (uMods & SHIFT_PRESSED)
888 abKeystate[VK_SHIFT] = 0x80;
889 if (uMods & CAPSLOCK_ON)
890 abKeystate[VK_CAPITAL] = 1;
891
892 if ((uMods & ALT_GR) == ALT_GR)
893 {
894 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
895 abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
896 }
897
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200898 s_iIsDead = ToUnicode(pker->wVirtualKeyCode, pker->wVirtualScanCode,
899 abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000900
901 if (s_iIsDead > 0)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200902 pker->UChar = (WCHAR) awAnsiCode[0];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000903
904 return s_iIsDead;
905}
906
907#ifdef _MSC_VER
908/* MUST switch optimization on again here, otherwise a call to
909 * decode_key_event() may crash (e.g. when hitting caps-lock) */
910# pragma optimize("", on)
Bram Moolenaar7b5f8322006-03-23 22:47:08 +0000911# pragma warning(pop)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000912
913# if (_MSC_VER < 1100)
914/* MUST turn off global optimisation for this next function, or
915 * pressing ctrl-minus in insert mode crashes Vim when built with
916 * VC4.1. -- negri. */
917# pragma optimize("g", off)
918# endif
919#endif
920
921static BOOL g_fJustGotFocus = FALSE;
922
923/*
924 * Decode a KEY_EVENT into one or two keystrokes
925 */
926 static BOOL
927decode_key_event(
928 KEY_EVENT_RECORD *pker,
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200929 WCHAR *pch,
930 WCHAR *pch2,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000931 int *pmodifiers,
932 BOOL fDoPost)
933{
934 int i;
935 const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
936
937 *pch = *pch2 = NUL;
938 g_fJustGotFocus = FALSE;
939
940 /* ignore key up events */
941 if (!pker->bKeyDown)
942 return FALSE;
943
944 /* ignore some keystrokes */
945 switch (pker->wVirtualKeyCode)
946 {
947 /* modifiers */
948 case VK_SHIFT:
949 case VK_CONTROL:
950 case VK_MENU: /* Alt key */
951 return FALSE;
952
953 default:
954 break;
955 }
956
957 /* special cases */
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200958 if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->UChar == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000959 {
960 /* Ctrl-6 is Ctrl-^ */
961 if (pker->wVirtualKeyCode == '6')
962 {
963 *pch = Ctrl_HAT;
964 return TRUE;
965 }
966 /* Ctrl-2 is Ctrl-@ */
967 else if (pker->wVirtualKeyCode == '2')
968 {
969 *pch = NUL;
970 return TRUE;
971 }
972 /* Ctrl-- is Ctrl-_ */
973 else if (pker->wVirtualKeyCode == 0xBD)
974 {
975 *pch = Ctrl__;
976 return TRUE;
977 }
978 }
979
980 /* Shift-TAB */
981 if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED))
982 {
983 *pch = K_NUL;
984 *pch2 = '\017';
985 return TRUE;
986 }
987
988 for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]); --i >= 0; )
989 {
990 if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
991 {
992 if (nModifs == 0)
993 *pch = VirtKeyMap[i].chAlone;
994 else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
995 *pch = VirtKeyMap[i].chShift;
996 else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
997 *pch = VirtKeyMap[i].chCtrl;
998 else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
999 *pch = VirtKeyMap[i].chAlt;
1000
1001 if (*pch != 0)
1002 {
1003 if (VirtKeyMap[i].fAnsiKey)
1004 {
1005 *pch2 = *pch;
1006 *pch = K_NUL;
1007 }
1008
1009 return TRUE;
1010 }
1011 }
1012 }
1013
1014 i = win32_kbd_patch_key(pker);
1015
1016 if (i < 0)
1017 *pch = NUL;
1018 else
1019 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001020 *pch = (i > 0) ? pker->UChar : NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001021
1022 if (pmodifiers != NULL)
1023 {
1024 /* Pass on the ALT key as a modifier, but only when not combined
1025 * with CTRL (which is ALTGR). */
1026 if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0)
1027 *pmodifiers |= MOD_MASK_ALT;
1028
1029 /* Pass on SHIFT only for special keys, because we don't know when
1030 * it's already included with the character. */
1031 if ((nModifs & SHIFT) != 0 && *pch <= 0x20)
1032 *pmodifiers |= MOD_MASK_SHIFT;
1033
1034 /* Pass on CTRL only for non-special keys, because we don't know
1035 * when it's already included with the character. And not when
1036 * combined with ALT (which is ALTGR). */
1037 if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0
1038 && *pch >= 0x20 && *pch < 0x80)
1039 *pmodifiers |= MOD_MASK_CTRL;
1040 }
1041 }
1042
1043 return (*pch != NUL);
1044}
1045
1046#ifdef _MSC_VER
1047# pragma optimize("", on)
1048#endif
1049
1050#endif /* FEAT_GUI_W32 */
1051
1052
1053#ifdef FEAT_MOUSE
1054
1055/*
1056 * For the GUI the mouse handling is in gui_w32.c.
1057 */
1058# ifdef FEAT_GUI_W32
1059 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01001060mch_setmouse(int on UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001061{
1062}
1063# else
1064static int g_fMouseAvail = FALSE; /* mouse present */
1065static int g_fMouseActive = FALSE; /* mouse enabled */
1066static int g_nMouseClick = -1; /* mouse status */
1067static int g_xMouse; /* mouse x coordinate */
1068static int g_yMouse; /* mouse y coordinate */
1069
1070/*
1071 * Enable or disable mouse input
1072 */
1073 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001074mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001075{
1076 DWORD cmodein;
1077
1078 if (!g_fMouseAvail)
1079 return;
1080
1081 g_fMouseActive = on;
1082 GetConsoleMode(g_hConIn, &cmodein);
1083
1084 if (g_fMouseActive)
1085 cmodein |= ENABLE_MOUSE_INPUT;
1086 else
1087 cmodein &= ~ENABLE_MOUSE_INPUT;
1088
1089 SetConsoleMode(g_hConIn, cmodein);
1090}
1091
Bram Moolenaar071d4272004-06-13 20:20:40 +00001092/*
1093 * Decode a MOUSE_EVENT. If it's a valid event, return MOUSE_LEFT,
1094 * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
1095 * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
1096 * or a MOUSE_LEFT, _MIDDLE, or _RIGHT. We encode the button type,
1097 * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
1098 * and we return the mouse position in g_xMouse and g_yMouse.
1099 *
1100 * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
1101 * MOUSE_DRAGs and one MOUSE_RELEASE. MOUSE_RELEASE will be followed only
1102 * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
1103 *
1104 * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
1105 * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
1106 *
1107 * Windows will send us MOUSE_MOVED notifications whenever the mouse
1108 * moves, even if it stays within the same character cell. We ignore
1109 * all MOUSE_MOVED messages if the position hasn't really changed, and
1110 * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
1111 * we're only interested in MOUSE_DRAG).
1112 *
1113 * All of this is complicated by the code that fakes MOUSE_MIDDLE on
1114 * 2-button mouses by pressing the left & right buttons simultaneously.
1115 * In practice, it's almost impossible to click both at the same time,
1116 * so we need to delay a little. Also, we tend not to get MOUSE_RELEASE
1117 * in such cases, if the user is clicking quickly.
1118 */
1119 static BOOL
1120decode_mouse_event(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001121 MOUSE_EVENT_RECORD *pmer)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001122{
1123 static int s_nOldButton = -1;
1124 static int s_nOldMouseClick = -1;
1125 static int s_xOldMouse = -1;
1126 static int s_yOldMouse = -1;
1127 static linenr_T s_old_topline = 0;
1128#ifdef FEAT_DIFF
1129 static int s_old_topfill = 0;
1130#endif
1131 static int s_cClicks = 1;
1132 static BOOL s_fReleased = TRUE;
1133 static DWORD s_dwLastClickTime = 0;
1134 static BOOL s_fNextIsMiddle = FALSE;
1135
1136 static DWORD cButtons = 0; /* number of buttons supported */
1137
1138 const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
1139 const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
1140 const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
1141 const DWORD LEFT_RIGHT = LEFT | RIGHT;
1142
1143 int nButton;
1144
1145 if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons))
1146 cButtons = 2;
1147
1148 if (!g_fMouseAvail || !g_fMouseActive)
1149 {
1150 g_nMouseClick = -1;
1151 return FALSE;
1152 }
1153
1154 /* get a spurious MOUSE_EVENT immediately after receiving focus; ignore */
1155 if (g_fJustGotFocus)
1156 {
1157 g_fJustGotFocus = FALSE;
1158 return FALSE;
1159 }
1160
1161 /* unprocessed mouse click? */
1162 if (g_nMouseClick != -1)
1163 return TRUE;
1164
1165 nButton = -1;
1166 g_xMouse = pmer->dwMousePosition.X;
1167 g_yMouse = pmer->dwMousePosition.Y;
1168
1169 if (pmer->dwEventFlags == MOUSE_MOVED)
1170 {
1171 /* ignore MOUSE_MOVED events if (x, y) hasn't changed. (We get these
1172 * events even when the mouse moves only within a char cell.) */
1173 if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse)
1174 return FALSE;
1175 }
1176
1177 /* If no buttons are pressed... */
1178 if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0)
1179 {
1180 /* If the last thing returned was MOUSE_RELEASE, ignore this */
1181 if (s_fReleased)
1182 return FALSE;
1183
1184 nButton = MOUSE_RELEASE;
1185 s_fReleased = TRUE;
1186 }
1187 else /* one or more buttons pressed */
1188 {
1189 /* on a 2-button mouse, hold down left and right buttons
1190 * simultaneously to get MIDDLE. */
1191
1192 if (cButtons == 2 && s_nOldButton != MOUSE_DRAG)
1193 {
1194 DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
1195
1196 /* if either left or right button only is pressed, see if the
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02001197 * next mouse event has both of them pressed */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198 if (dwLR == LEFT || dwLR == RIGHT)
1199 {
1200 for (;;)
1201 {
1202 /* wait a short time for next input event */
1203 if (WaitForSingleObject(g_hConIn, p_mouset / 3)
1204 != WAIT_OBJECT_0)
1205 break;
1206 else
1207 {
1208 DWORD cRecords = 0;
1209 INPUT_RECORD ir;
1210 MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
1211
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001212 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001213
1214 if (cRecords == 0 || ir.EventType != MOUSE_EVENT
1215 || !(pmer2->dwButtonState & LEFT_RIGHT))
1216 break;
1217 else
1218 {
1219 if (pmer2->dwEventFlags != MOUSE_MOVED)
1220 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001221 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001222
1223 return decode_mouse_event(pmer2);
1224 }
1225 else if (s_xOldMouse == pmer2->dwMousePosition.X &&
1226 s_yOldMouse == pmer2->dwMousePosition.Y)
1227 {
1228 /* throw away spurious mouse move */
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001229 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230
1231 /* are there any more mouse events in queue? */
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001232 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001233
1234 if (cRecords==0 || ir.EventType != MOUSE_EVENT)
1235 break;
1236 }
1237 else
1238 break;
1239 }
1240 }
1241 }
1242 }
1243 }
1244
1245 if (s_fNextIsMiddle)
1246 {
1247 nButton = (pmer->dwEventFlags == MOUSE_MOVED)
1248 ? MOUSE_DRAG : MOUSE_MIDDLE;
1249 s_fNextIsMiddle = FALSE;
1250 }
1251 else if (cButtons == 2 &&
1252 ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
1253 {
1254 nButton = MOUSE_MIDDLE;
1255
1256 if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED)
1257 {
1258 s_fNextIsMiddle = TRUE;
1259 nButton = MOUSE_RELEASE;
1260 }
1261 }
1262 else if ((pmer->dwButtonState & LEFT) == LEFT)
1263 nButton = MOUSE_LEFT;
1264 else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
1265 nButton = MOUSE_MIDDLE;
1266 else if ((pmer->dwButtonState & RIGHT) == RIGHT)
1267 nButton = MOUSE_RIGHT;
1268
1269 if (! s_fReleased && ! s_fNextIsMiddle
1270 && nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG)
1271 return FALSE;
1272
1273 s_fReleased = s_fNextIsMiddle;
1274 }
1275
1276 if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK)
1277 {
1278 /* button pressed or released, without mouse moving */
1279 if (nButton != -1 && nButton != MOUSE_RELEASE)
1280 {
1281 DWORD dwCurrentTime = GetTickCount();
1282
1283 if (s_xOldMouse != g_xMouse
1284 || s_yOldMouse != g_yMouse
1285 || s_nOldButton != nButton
1286 || s_old_topline != curwin->w_topline
1287#ifdef FEAT_DIFF
1288 || s_old_topfill != curwin->w_topfill
1289#endif
1290 || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset)
1291 {
1292 s_cClicks = 1;
1293 }
1294 else if (++s_cClicks > 4)
1295 {
1296 s_cClicks = 1;
1297 }
1298
1299 s_dwLastClickTime = dwCurrentTime;
1300 }
1301 }
1302 else if (pmer->dwEventFlags == MOUSE_MOVED)
1303 {
1304 if (nButton != -1 && nButton != MOUSE_RELEASE)
1305 nButton = MOUSE_DRAG;
1306
1307 s_cClicks = 1;
1308 }
1309
1310 if (nButton == -1)
1311 return FALSE;
1312
1313 if (nButton != MOUSE_RELEASE)
1314 s_nOldButton = nButton;
1315
1316 g_nMouseClick = nButton;
1317
1318 if (pmer->dwControlKeyState & SHIFT_PRESSED)
1319 g_nMouseClick |= MOUSE_SHIFT;
1320 if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
1321 g_nMouseClick |= MOUSE_CTRL;
1322 if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
1323 g_nMouseClick |= MOUSE_ALT;
1324
1325 if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE)
1326 SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
1327
1328 /* only pass on interesting (i.e., different) mouse events */
1329 if (s_xOldMouse == g_xMouse
1330 && s_yOldMouse == g_yMouse
1331 && s_nOldMouseClick == g_nMouseClick)
1332 {
1333 g_nMouseClick = -1;
1334 return FALSE;
1335 }
1336
1337 s_xOldMouse = g_xMouse;
1338 s_yOldMouse = g_yMouse;
1339 s_old_topline = curwin->w_topline;
1340#ifdef FEAT_DIFF
1341 s_old_topfill = curwin->w_topfill;
1342#endif
1343 s_nOldMouseClick = g_nMouseClick;
1344
1345 return TRUE;
1346}
1347
1348# endif /* FEAT_GUI_W32 */
1349#endif /* FEAT_MOUSE */
1350
1351
1352#ifdef MCH_CURSOR_SHAPE
1353/*
1354 * Set the shape of the cursor.
1355 * 'thickness' can be from 1 (thin) to 99 (block)
1356 */
1357 static void
1358mch_set_cursor_shape(int thickness)
1359{
1360 CONSOLE_CURSOR_INFO ConsoleCursorInfo;
1361 ConsoleCursorInfo.dwSize = thickness;
1362 ConsoleCursorInfo.bVisible = s_cursor_visible;
1363
1364 SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo);
1365 if (s_cursor_visible)
1366 SetConsoleCursorPosition(g_hConOut, g_coord);
1367}
1368
1369 void
1370mch_update_cursor(void)
1371{
1372 int idx;
1373 int thickness;
1374
1375 /*
1376 * How the cursor is drawn depends on the current mode.
1377 */
1378 idx = get_shape_idx(FALSE);
1379
1380 if (shape_table[idx].shape == SHAPE_BLOCK)
1381 thickness = 99; /* 100 doesn't work on W95 */
1382 else
1383 thickness = shape_table[idx].percentage;
1384 mch_set_cursor_shape(thickness);
1385}
1386#endif
1387
1388#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1389/*
1390 * Handle FOCUS_EVENT.
1391 */
1392 static void
1393handle_focus_event(INPUT_RECORD ir)
1394{
1395 g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
1396 ui_focus_change((int)g_fJustGotFocus);
1397}
1398
1399/*
1400 * Wait until console input from keyboard or mouse is available,
1401 * or the time is up.
1402 * Return TRUE if something is available FALSE if not.
1403 */
1404 static int
1405WaitForChar(long msec)
1406{
1407 DWORD dwNow = 0, dwEndTime = 0;
1408 INPUT_RECORD ir;
1409 DWORD cRecords;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001410 WCHAR ch, ch2;
Bram Moolenaar4445f7e2016-04-20 20:55:56 +02001411#ifdef FEAT_TIMERS
Bram Moolenaar40b1b542016-04-20 20:18:23 +02001412 int tb_change_cnt = typebuf.tb_change_cnt;
Bram Moolenaar4445f7e2016-04-20 20:55:56 +02001413#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001414
1415 if (msec > 0)
1416 /* Wait until the specified time has elapsed. */
1417 dwEndTime = GetTickCount() + msec;
1418 else if (msec < 0)
1419 /* Wait forever. */
1420 dwEndTime = INFINITE;
1421
1422 /* We need to loop until the end of the time period, because
1423 * we might get multiple unusable mouse events in that time.
1424 */
1425 for (;;)
1426 {
Bram Moolenaarca568ae2016-02-01 21:32:58 +01001427#ifdef MESSAGE_QUEUE
1428 parse_queued_messages();
1429#endif
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001430#ifdef FEAT_MZSCHEME
1431 mzvim_check_threads();
1432#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001433#ifdef FEAT_CLIENTSERVER
1434 serverProcessPendingMessages();
1435#endif
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001436
Bram Moolenaar071d4272004-06-13 20:20:40 +00001437 if (0
1438#ifdef FEAT_MOUSE
1439 || g_nMouseClick != -1
1440#endif
1441#ifdef FEAT_CLIENTSERVER
1442 || input_available()
1443#endif
1444 )
1445 return TRUE;
1446
1447 if (msec > 0)
1448 {
Bram Moolenaarb7512b72013-08-10 12:45:09 +02001449 /* If the specified wait time has passed, return. Beware that
1450 * GetTickCount() may wrap around (overflow). */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451 dwNow = GetTickCount();
Bram Moolenaarb7512b72013-08-10 12:45:09 +02001452 if ((int)(dwNow - dwEndTime) >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001453 break;
1454 }
1455 if (msec != 0)
1456 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001457 DWORD dwWaitTime = dwEndTime - dwNow;
1458
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01001459#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar8a8199e2016-11-26 15:13:33 +01001460 /* Check channel while waiting for input. */
Bram Moolenaar9186a272016-02-23 19:34:01 +01001461 if (dwWaitTime > 100)
Bram Moolenaar8a8199e2016-11-26 15:13:33 +01001462 {
Bram Moolenaar9186a272016-02-23 19:34:01 +01001463 dwWaitTime = 100;
Bram Moolenaar8a8199e2016-11-26 15:13:33 +01001464 /* If there is readahead then parse_queued_messages() timed out
1465 * and we should call it again soon. */
1466 if (channel_any_readahead())
1467 dwWaitTime = 10;
1468 }
Bram Moolenaar9186a272016-02-23 19:34:01 +01001469#endif
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001470#ifdef FEAT_MZSCHEME
1471 if (mzthreads_allowed() && p_mzq > 0
1472 && (msec < 0 || (long)dwWaitTime > p_mzq))
1473 dwWaitTime = p_mzq; /* don't wait longer than 'mzquantum' */
1474#endif
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001475#ifdef FEAT_TIMERS
1476 {
1477 long due_time;
1478
1479 /* When waiting very briefly don't trigger timers. */
1480 if (dwWaitTime > 10)
1481 {
1482 /* Trigger timers and then get the time in msec until the
1483 * next one is due. Wait up to that time. */
1484 due_time = check_due_timer();
Bram Moolenaar40b1b542016-04-20 20:18:23 +02001485 if (typebuf.tb_change_cnt != tb_change_cnt)
1486 {
1487 /* timer may have used feedkeys() */
1488 return FALSE;
1489 }
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001490 if (due_time > 0 && dwWaitTime > (DWORD)due_time)
1491 dwWaitTime = due_time;
1492 }
1493 }
1494#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495#ifdef FEAT_CLIENTSERVER
1496 /* Wait for either an event on the console input or a message in
1497 * the client-server window. */
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +02001498 if (msg_wait_for_multiple_objects(1, &g_hConIn, FALSE,
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001499 dwWaitTime, QS_SENDMESSAGE) != WAIT_OBJECT_0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500#else
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +02001501 if (wait_for_single_object(g_hConIn, dwWaitTime) != WAIT_OBJECT_0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001502#endif
1503 continue;
1504 }
1505
1506 cRecords = 0;
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001507 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508
1509#ifdef FEAT_MBYTE_IME
1510 if (State & CMDLINE && msg_row == Rows - 1)
1511 {
1512 CONSOLE_SCREEN_BUFFER_INFO csbi;
1513
1514 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1515 {
1516 if (csbi.dwCursorPosition.Y != msg_row)
1517 {
1518 /* The screen is now messed up, must redraw the
1519 * command line and later all the windows. */
1520 redraw_all_later(CLEAR);
1521 cmdline_row -= (msg_row - csbi.dwCursorPosition.Y);
1522 redrawcmd();
1523 }
1524 }
1525 }
1526#endif
1527
1528 if (cRecords > 0)
1529 {
1530 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
1531 {
1532#ifdef FEAT_MBYTE_IME
1533 /* Windows IME sends two '\n's with only one 'ENTER'. First:
1534 * wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001535 if (ir.Event.KeyEvent.UChar == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001536 && ir.Event.KeyEvent.wVirtualKeyCode == 13)
1537 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001538 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001539 continue;
1540 }
1541#endif
1542 if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
1543 NULL, FALSE))
1544 return TRUE;
1545 }
1546
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001547 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001548
1549 if (ir.EventType == FOCUS_EVENT)
1550 handle_focus_event(ir);
1551 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1552 shell_resized();
1553#ifdef FEAT_MOUSE
1554 else if (ir.EventType == MOUSE_EVENT
1555 && decode_mouse_event(&ir.Event.MouseEvent))
1556 return TRUE;
1557#endif
1558 }
1559 else if (msec == 0)
1560 break;
1561 }
1562
1563#ifdef FEAT_CLIENTSERVER
1564 /* Something might have been received while we were waiting. */
1565 if (input_available())
1566 return TRUE;
1567#endif
Bram Moolenaarf12d9832016-01-29 21:11:25 +01001568
Bram Moolenaar071d4272004-06-13 20:20:40 +00001569 return FALSE;
1570}
1571
1572#ifndef FEAT_GUI_MSWIN
1573/*
1574 * return non-zero if a character is available
1575 */
1576 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001577mch_char_avail(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001578{
1579 return WaitForChar(0L);
1580}
1581#endif
1582
1583/*
1584 * Create the console input. Used when reading stdin doesn't work.
1585 */
1586 static void
1587create_conin(void)
1588{
1589 g_hConIn = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
1590 FILE_SHARE_READ|FILE_SHARE_WRITE,
1591 (LPSECURITY_ATTRIBUTES) NULL,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001592 OPEN_EXISTING, 0, (HANDLE)NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001593 did_create_conin = TRUE;
1594}
1595
1596/*
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01001597 * Get a keystroke or a mouse event, use a blocking wait.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001598 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001599 static WCHAR
1600tgetch(int *pmodifiers, WCHAR *pch2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001601{
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001602 WCHAR ch;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603
1604 for (;;)
1605 {
1606 INPUT_RECORD ir;
1607 DWORD cRecords = 0;
1608
1609#ifdef FEAT_CLIENTSERVER
1610 (void)WaitForChar(-1L);
1611 if (input_available())
1612 return 0;
1613# ifdef FEAT_MOUSE
1614 if (g_nMouseClick != -1)
1615 return 0;
1616# endif
1617#endif
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001618 if (read_console_input(g_hConIn, &ir, 1, &cRecords) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001619 {
1620 if (did_create_conin)
1621 read_error_exit();
1622 create_conin();
1623 continue;
1624 }
1625
1626 if (ir.EventType == KEY_EVENT)
1627 {
1628 if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2,
1629 pmodifiers, TRUE))
1630 return ch;
1631 }
1632 else if (ir.EventType == FOCUS_EVENT)
1633 handle_focus_event(ir);
1634 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1635 shell_resized();
1636#ifdef FEAT_MOUSE
1637 else if (ir.EventType == MOUSE_EVENT)
1638 {
1639 if (decode_mouse_event(&ir.Event.MouseEvent))
1640 return 0;
1641 }
1642#endif
1643 }
1644}
1645#endif /* !FEAT_GUI_W32 */
1646
1647
1648/*
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02001649 * mch_inchar(): low-level input function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001650 * Get one or more characters from the keyboard or the mouse.
1651 * If time == 0, do not wait for characters.
1652 * If time == n, wait a short time for characters.
1653 * If time == -1, wait forever for characters.
1654 * Returns the number of characters read into buf.
1655 */
1656 int
1657mch_inchar(
Bram Moolenaar1266d672017-02-01 13:43:36 +01001658 char_u *buf UNUSED,
1659 int maxlen UNUSED,
1660 long time UNUSED,
1661 int tb_change_cnt UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001662{
1663#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1664
1665 int len;
1666 int c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001667#define TYPEAHEADLEN 20
1668 static char_u typeahead[TYPEAHEADLEN]; /* previously typed bytes. */
1669 static int typeaheadlen = 0;
1670
1671 /* First use any typeahead that was kept because "buf" was too small. */
1672 if (typeaheadlen > 0)
1673 goto theend;
1674
Bram Moolenaar071d4272004-06-13 20:20:40 +00001675 if (time >= 0)
1676 {
1677 if (!WaitForChar(time)) /* no character available */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001678 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001679 }
1680 else /* time == -1, wait forever */
1681 {
1682 mch_set_winsize_now(); /* Allow winsize changes from now on */
1683
Bram Moolenaar3918c952005-03-15 22:34:55 +00001684 /*
1685 * If there is no character available within 2 seconds (default)
1686 * write the autoscript file to disk. Or cause the CursorHold event
1687 * to be triggered.
1688 */
1689 if (!WaitForChar(p_ut))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001690 {
1691#ifdef FEAT_AUTOCMD
Bram Moolenaard35f9712005-12-18 22:02:33 +00001692 if (trigger_cursorhold() && maxlen >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001693 {
Bram Moolenaar3918c952005-03-15 22:34:55 +00001694 buf[0] = K_SPECIAL;
1695 buf[1] = KS_EXTRA;
1696 buf[2] = (int)KE_CURSORHOLD;
1697 return 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001698 }
1699#endif
Bram Moolenaar702517d2005-06-27 22:34:07 +00001700 before_blocking();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001701 }
1702 }
1703
1704 /*
1705 * Try to read as many characters as there are, until the buffer is full.
1706 */
1707
1708 /* we will get at least one key. Get more if they are available. */
1709 g_fCBrkPressed = FALSE;
1710
1711#ifdef MCH_WRITE_DUMP
1712 if (fdDump)
1713 fputc('[', fdDump);
1714#endif
1715
1716 /* Keep looping until there is something in the typeahead buffer and more
1717 * to get and still room in the buffer (up to two bytes for a char and
1718 * three bytes for a modifier). */
1719 while ((typeaheadlen == 0 || WaitForChar(0L))
1720 && typeaheadlen + 5 <= TYPEAHEADLEN)
1721 {
1722 if (typebuf_changed(tb_change_cnt))
1723 {
1724 /* "buf" may be invalid now if a client put something in the
1725 * typeahead buffer and "buf" is in the typeahead buffer. */
1726 typeaheadlen = 0;
1727 break;
1728 }
1729#ifdef FEAT_MOUSE
1730 if (g_nMouseClick != -1)
1731 {
1732# ifdef MCH_WRITE_DUMP
1733 if (fdDump)
1734 fprintf(fdDump, "{%02x @ %d, %d}",
1735 g_nMouseClick, g_xMouse, g_yMouse);
1736# endif
1737 typeahead[typeaheadlen++] = ESC + 128;
1738 typeahead[typeaheadlen++] = 'M';
1739 typeahead[typeaheadlen++] = g_nMouseClick;
1740 typeahead[typeaheadlen++] = g_xMouse + '!';
1741 typeahead[typeaheadlen++] = g_yMouse + '!';
1742 g_nMouseClick = -1;
1743 }
1744 else
1745#endif
1746 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001747 WCHAR ch2 = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001748 int modifiers = 0;
1749
1750 c = tgetch(&modifiers, &ch2);
1751
1752 if (typebuf_changed(tb_change_cnt))
1753 {
1754 /* "buf" may be invalid now if a client put something in the
1755 * typeahead buffer and "buf" is in the typeahead buffer. */
1756 typeaheadlen = 0;
1757 break;
1758 }
1759
1760 if (c == Ctrl_C && ctrl_c_interrupts)
1761 {
1762#if defined(FEAT_CLIENTSERVER)
1763 trash_input_buf();
1764#endif
1765 got_int = TRUE;
1766 }
1767
1768#ifdef FEAT_MOUSE
1769 if (g_nMouseClick == -1)
1770#endif
1771 {
1772 int n = 1;
Bram Moolenaar45500912014-07-09 20:51:07 +02001773 int conv = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001774
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001775#ifdef FEAT_MBYTE
1776 if (ch2 == NUL)
1777 {
1778 int i;
1779 char_u *p;
1780 WCHAR ch[2];
1781
1782 ch[0] = c;
1783 if (c >= 0xD800 && c <= 0xDBFF) /* High surrogate */
1784 {
1785 ch[1] = tgetch(&modifiers, &ch2);
1786 n++;
1787 }
1788 p = utf16_to_enc(ch, &n);
1789 if (p != NULL)
1790 {
1791 for (i = 0; i < n; i++)
1792 typeahead[typeaheadlen + i] = p[i];
1793 vim_free(p);
1794 }
1795 }
1796 else
1797#endif
1798 typeahead[typeaheadlen] = c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799 if (ch2 != NUL)
1800 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001801 typeahead[typeaheadlen + n] = 3;
1802 typeahead[typeaheadlen + n + 1] = (char_u)ch2;
Bram Moolenaar45500912014-07-09 20:51:07 +02001803 n += 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001805
Bram Moolenaar45500912014-07-09 20:51:07 +02001806 if (conv)
1807 {
1808 char_u *p = typeahead + typeaheadlen;
Bram Moolenaar45500912014-07-09 20:51:07 +02001809
Bram Moolenaar1ec4dd42015-01-20 19:39:35 +01001810 if (*p != K_NUL)
Bram Moolenaar45500912014-07-09 20:51:07 +02001811 {
Bram Moolenaar1ec4dd42015-01-20 19:39:35 +01001812 char_u *e = typeahead + TYPEAHEADLEN;
1813
1814 while (*p && p < e)
Bram Moolenaar45500912014-07-09 20:51:07 +02001815 {
Bram Moolenaar1ec4dd42015-01-20 19:39:35 +01001816 if (*p == K_NUL)
1817 {
1818 ++p;
1819 mch_memmove(p + 1, p, ((size_t)(e - p)) - 1);
1820 *p = 3;
1821 ++n;
1822 }
Bram Moolenaar45500912014-07-09 20:51:07 +02001823 ++p;
Bram Moolenaar45500912014-07-09 20:51:07 +02001824 }
Bram Moolenaar45500912014-07-09 20:51:07 +02001825 }
1826 }
1827
Bram Moolenaar071d4272004-06-13 20:20:40 +00001828 /* Use the ALT key to set the 8th bit of the character
1829 * when it's one byte, the 8th bit isn't set yet and not
1830 * using a double-byte encoding (would become a lead
1831 * byte). */
1832 if ((modifiers & MOD_MASK_ALT)
1833 && n == 1
1834 && (typeahead[typeaheadlen] & 0x80) == 0
1835#ifdef FEAT_MBYTE
1836 && !enc_dbcs
1837#endif
1838 )
1839 {
Bram Moolenaar85a3e5c2007-11-20 16:22:16 +00001840#ifdef FEAT_MBYTE
1841 n = (*mb_char2bytes)(typeahead[typeaheadlen] | 0x80,
1842 typeahead + typeaheadlen);
1843#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001844 typeahead[typeaheadlen] |= 0x80;
Bram Moolenaar85a3e5c2007-11-20 16:22:16 +00001845#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001846 modifiers &= ~MOD_MASK_ALT;
1847 }
1848
1849 if (modifiers != 0)
1850 {
1851 /* Prepend modifiers to the character. */
1852 mch_memmove(typeahead + typeaheadlen + 3,
1853 typeahead + typeaheadlen, n);
1854 typeahead[typeaheadlen++] = K_SPECIAL;
1855 typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
1856 typeahead[typeaheadlen++] = modifiers;
1857 }
1858
1859 typeaheadlen += n;
1860
1861#ifdef MCH_WRITE_DUMP
1862 if (fdDump)
1863 fputc(c, fdDump);
1864#endif
1865 }
1866 }
1867 }
1868
1869#ifdef MCH_WRITE_DUMP
1870 if (fdDump)
1871 {
1872 fputs("]\n", fdDump);
1873 fflush(fdDump);
1874 }
1875#endif
1876
Bram Moolenaar071d4272004-06-13 20:20:40 +00001877theend:
1878 /* Move typeahead to "buf", as much as fits. */
1879 len = 0;
1880 while (len < maxlen && typeaheadlen > 0)
1881 {
1882 buf[len++] = typeahead[0];
1883 mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
1884 }
1885 return len;
1886
1887#else /* FEAT_GUI_W32 */
1888 return 0;
1889#endif /* FEAT_GUI_W32 */
1890}
1891
Bram Moolenaar82881492012-11-20 16:53:39 +01001892#ifndef PROTO
1893# ifndef __MINGW32__
1894# include <shellapi.h> /* required for FindExecutable() */
1895# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001896#endif
1897
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001898/*
1899 * Return TRUE if "name" is in $PATH.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00001900 * TODO: Should somehow check if it's really executable.
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001901 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001902 static int
Bram Moolenaarc7f02552014-04-01 21:00:59 +02001903executable_exists(char *name, char_u **path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001904{
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001905 char *dum;
1906 char fname[_MAX_PATH];
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02001907 char *curpath, *newpath;
1908 long n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001909
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001910#ifdef FEAT_MBYTE
1911 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001912 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001913 WCHAR *p = enc_to_utf16((char_u *)name, NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001914 WCHAR fnamew[_MAX_PATH];
1915 WCHAR *dumw;
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02001916 WCHAR *wcurpath, *wnewpath;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001917
1918 if (p != NULL)
1919 {
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02001920 wcurpath = _wgetenv(L"PATH");
1921 wnewpath = (WCHAR*)alloc((unsigned)(wcslen(wcurpath) + 3)
1922 * sizeof(WCHAR));
1923 if (wnewpath == NULL)
1924 return FALSE;
1925 wcscpy(wnewpath, L".;");
1926 wcscat(wnewpath, wcurpath);
1927 n = (long)SearchPathW(wnewpath, p, NULL, _MAX_PATH, fnamew, &dumw);
1928 vim_free(wnewpath);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001929 vim_free(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001930 if (n == 0)
1931 return FALSE;
1932 if (GetFileAttributesW(fnamew) & FILE_ATTRIBUTE_DIRECTORY)
1933 return FALSE;
1934 if (path != NULL)
1935 *path = utf16_to_enc(fnamew, NULL);
1936 return TRUE;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001937 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001938 }
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001939#endif
Bram Moolenaarc40bdee2014-08-29 17:45:32 +02001940
1941 curpath = getenv("PATH");
1942 newpath = (char*)alloc((unsigned)(STRLEN(curpath) + 3));
1943 if (newpath == NULL)
1944 return FALSE;
1945 STRCPY(newpath, ".;");
1946 STRCAT(newpath, curpath);
1947 n = (long)SearchPath(newpath, name, NULL, _MAX_PATH, fname, &dum);
1948 vim_free(newpath);
1949 if (n == 0)
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001950 return FALSE;
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001951 if (mch_isdir((char_u *)fname))
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001952 return FALSE;
Bram Moolenaarc7f02552014-04-01 21:00:59 +02001953 if (path != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001954 *path = vim_strsave((char_u *)fname);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001955 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001956}
1957
Bram Moolenaard32a99a2010-09-21 17:29:23 +02001958#if ((defined(__MINGW32__) || defined (__CYGWIN32__)) && \
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02001959 __MSVCRT_VERSION__ >= 0x800) || (defined(_MSC_VER) && _MSC_VER >= 1400)
Bram Moolenaard32a99a2010-09-21 17:29:23 +02001960/*
1961 * Bad parameter handler.
1962 *
1963 * Certain MS CRT functions will intentionally crash when passed invalid
1964 * parameters to highlight possible security holes. Setting this function as
1965 * the bad parameter handler will prevent the crash.
1966 *
1967 * In debug builds the parameters contain CRT information that might help track
1968 * down the source of a problem, but in non-debug builds the arguments are all
1969 * NULL/0. Debug builds will also produce assert dialogs from the CRT, it is
1970 * worth allowing these to make debugging of issues easier.
1971 */
1972 static void
1973bad_param_handler(const wchar_t *expression,
1974 const wchar_t *function,
1975 const wchar_t *file,
1976 unsigned int line,
1977 uintptr_t pReserved)
1978{
1979}
1980
1981# define SET_INVALID_PARAM_HANDLER \
1982 ((void)_set_invalid_parameter_handler(bad_param_handler))
1983#else
1984# define SET_INVALID_PARAM_HANDLER
1985#endif
1986
Bram Moolenaar071d4272004-06-13 20:20:40 +00001987#ifdef FEAT_GUI_W32
1988
1989/*
1990 * GUI version of mch_init().
1991 */
1992 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001993mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001994{
1995#ifndef __MINGW32__
1996 extern int _fmode;
1997#endif
1998
Bram Moolenaard32a99a2010-09-21 17:29:23 +02001999 /* Silently handle invalid parameters to CRT functions */
2000 SET_INVALID_PARAM_HANDLER;
2001
Bram Moolenaar071d4272004-06-13 20:20:40 +00002002 /* Let critical errors result in a failure, not in a dialog box. Required
2003 * for the timestamp test to work on removed floppies. */
2004 SetErrorMode(SEM_FAILCRITICALERRORS);
2005
2006 _fmode = O_BINARY; /* we do our own CR-LF translation */
2007
2008 /* Specify window size. Is there a place to get the default from? */
2009 Rows = 25;
2010 Columns = 80;
2011
2012 /* Look for 'vimrun' */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002013 {
2014 char_u vimrun_location[_MAX_PATH + 4];
2015
2016 /* First try in same directory as gvim.exe */
2017 STRCPY(vimrun_location, exe_name);
2018 STRCPY(gettail(vimrun_location), "vimrun.exe");
2019 if (mch_getperm(vimrun_location) >= 0)
2020 {
2021 if (*skiptowhite(vimrun_location) != NUL)
2022 {
2023 /* Enclose path with white space in double quotes. */
2024 mch_memmove(vimrun_location + 1, vimrun_location,
2025 STRLEN(vimrun_location) + 1);
2026 *vimrun_location = '"';
2027 STRCPY(gettail(vimrun_location), "vimrun\" ");
2028 }
2029 else
2030 STRCPY(gettail(vimrun_location), "vimrun ");
2031
2032 vimrun_path = (char *)vim_strsave(vimrun_location);
2033 s_dont_use_vimrun = FALSE;
2034 }
Bram Moolenaarc7f02552014-04-01 21:00:59 +02002035 else if (executable_exists("vimrun.exe", NULL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036 s_dont_use_vimrun = FALSE;
2037
2038 /* Don't give the warning for a missing vimrun.exe right now, but only
2039 * when vimrun was supposed to be used. Don't bother people that do
2040 * not need vimrun.exe. */
2041 if (s_dont_use_vimrun)
2042 need_vimrun_warning = TRUE;
2043 }
2044
2045 /*
2046 * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
2047 * Otherwise the default "findstr /n" is used.
2048 */
Bram Moolenaarc7f02552014-04-01 21:00:59 +02002049 if (!executable_exists("findstr.exe", NULL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002050 set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
2051
2052#ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002053 win_clip_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002054#endif
2055}
2056
2057
2058#else /* FEAT_GUI_W32 */
2059
2060#define SRWIDTH(sr) ((sr).Right - (sr).Left + 1)
2061#define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1)
2062
2063/*
2064 * ClearConsoleBuffer()
2065 * Description:
2066 * Clears the entire contents of the console screen buffer, using the
2067 * specified attribute.
2068 * Returns:
2069 * TRUE on success
2070 */
2071 static BOOL
2072ClearConsoleBuffer(WORD wAttribute)
2073{
2074 CONSOLE_SCREEN_BUFFER_INFO csbi;
2075 COORD coord;
2076 DWORD NumCells, dummy;
2077
2078 if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2079 return FALSE;
2080
2081 NumCells = csbi.dwSize.X * csbi.dwSize.Y;
2082 coord.X = 0;
2083 coord.Y = 0;
2084 if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells,
2085 coord, &dummy))
2086 {
2087 return FALSE;
2088 }
2089 if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells,
2090 coord, &dummy))
2091 {
2092 return FALSE;
2093 }
2094
2095 return TRUE;
2096}
2097
2098/*
2099 * FitConsoleWindow()
2100 * Description:
2101 * Checks if the console window will fit within given buffer dimensions.
2102 * Also, if requested, will shrink the window to fit.
2103 * Returns:
2104 * TRUE on success
2105 */
2106 static BOOL
2107FitConsoleWindow(
2108 COORD dwBufferSize,
2109 BOOL WantAdjust)
2110{
2111 CONSOLE_SCREEN_BUFFER_INFO csbi;
2112 COORD dwWindowSize;
2113 BOOL NeedAdjust = FALSE;
2114
2115 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2116 {
2117 /*
2118 * A buffer resize will fail if the current console window does
2119 * not lie completely within that buffer. To avoid this, we might
2120 * have to move and possibly shrink the window.
2121 */
2122 if (csbi.srWindow.Right >= dwBufferSize.X)
2123 {
2124 dwWindowSize.X = SRWIDTH(csbi.srWindow);
2125 if (dwWindowSize.X > dwBufferSize.X)
2126 dwWindowSize.X = dwBufferSize.X;
2127 csbi.srWindow.Right = dwBufferSize.X - 1;
2128 csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X;
2129 NeedAdjust = TRUE;
2130 }
2131 if (csbi.srWindow.Bottom >= dwBufferSize.Y)
2132 {
2133 dwWindowSize.Y = SRHEIGHT(csbi.srWindow);
2134 if (dwWindowSize.Y > dwBufferSize.Y)
2135 dwWindowSize.Y = dwBufferSize.Y;
2136 csbi.srWindow.Bottom = dwBufferSize.Y - 1;
2137 csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y;
2138 NeedAdjust = TRUE;
2139 }
2140 if (NeedAdjust && WantAdjust)
2141 {
2142 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow))
2143 return FALSE;
2144 }
2145 return TRUE;
2146 }
2147
2148 return FALSE;
2149}
2150
2151typedef struct ConsoleBufferStruct
2152{
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002153 BOOL IsValid;
2154 CONSOLE_SCREEN_BUFFER_INFO Info;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002155 PCHAR_INFO Buffer;
2156 COORD BufferSize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002157} ConsoleBuffer;
2158
2159/*
2160 * SaveConsoleBuffer()
2161 * Description:
2162 * Saves important information about the console buffer, including the
2163 * actual buffer contents. The saved information is suitable for later
2164 * restoration by RestoreConsoleBuffer().
2165 * Returns:
2166 * TRUE if all information was saved; FALSE otherwise
2167 * If FALSE, still sets cb->IsValid if buffer characteristics were saved.
2168 */
2169 static BOOL
2170SaveConsoleBuffer(
2171 ConsoleBuffer *cb)
2172{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002173 DWORD NumCells;
2174 COORD BufferCoord;
2175 SMALL_RECT ReadRegion;
2176 WORD Y, Y_incr;
2177
Bram Moolenaar071d4272004-06-13 20:20:40 +00002178 if (cb == NULL)
2179 return FALSE;
2180
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002181 if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002182 {
2183 cb->IsValid = FALSE;
2184 return FALSE;
2185 }
2186 cb->IsValid = TRUE;
2187
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002188 /*
2189 * Allocate a buffer large enough to hold the entire console screen
2190 * buffer. If this ConsoleBuffer structure has already been initialized
2191 * with a buffer of the correct size, then just use that one.
2192 */
2193 if (!cb->IsValid || cb->Buffer == NULL ||
2194 cb->BufferSize.X != cb->Info.dwSize.X ||
2195 cb->BufferSize.Y != cb->Info.dwSize.Y)
2196 {
2197 cb->BufferSize.X = cb->Info.dwSize.X;
2198 cb->BufferSize.Y = cb->Info.dwSize.Y;
2199 NumCells = cb->BufferSize.X * cb->BufferSize.Y;
2200 vim_free(cb->Buffer);
2201 cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
2202 if (cb->Buffer == NULL)
2203 return FALSE;
2204 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002205
2206 /*
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002207 * We will now copy the console screen buffer into our buffer.
2208 * ReadConsoleOutput() seems to be limited as far as how much you
2209 * can read at a time. Empirically, this number seems to be about
2210 * 12000 cells (rows * columns). Start at position (0, 0) and copy
2211 * in chunks until it is all copied. The chunks will all have the
2212 * same horizontal characteristics, so initialize them now. The
2213 * height of each chunk will be (12000 / width).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002214 */
Bram Moolenaar61594242015-09-01 20:23:37 +02002215 BufferCoord.X = 0;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002216 ReadRegion.Left = 0;
2217 ReadRegion.Right = cb->Info.dwSize.X - 1;
2218 Y_incr = 12000 / cb->Info.dwSize.X;
2219 for (Y = 0; Y < cb->BufferSize.Y; Y += Y_incr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002220 {
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002221 /*
2222 * Read into position (0, Y) in our buffer.
2223 */
2224 BufferCoord.Y = Y;
2225 /*
2226 * Read the region whose top left corner is (0, Y) and whose bottom
2227 * right corner is (width - 1, Y + Y_incr - 1). This should define
2228 * a region of size width by Y_incr. Don't worry if this region is
2229 * too large for the remaining buffer; it will be cropped.
2230 */
2231 ReadRegion.Top = Y;
2232 ReadRegion.Bottom = Y + Y_incr - 1;
2233 if (!ReadConsoleOutput(g_hConOut, /* output handle */
2234 cb->Buffer, /* our buffer */
2235 cb->BufferSize, /* dimensions of our buffer */
2236 BufferCoord, /* offset in our buffer */
2237 &ReadRegion)) /* region to save */
2238 {
2239 vim_free(cb->Buffer);
2240 cb->Buffer = NULL;
2241 return FALSE;
2242 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002243 }
2244
2245 return TRUE;
2246}
2247
2248/*
2249 * RestoreConsoleBuffer()
2250 * Description:
2251 * Restores important information about the console buffer, including the
2252 * actual buffer contents, if desired. The information to restore is in
2253 * the same format used by SaveConsoleBuffer().
2254 * Returns:
2255 * TRUE on success
2256 */
2257 static BOOL
2258RestoreConsoleBuffer(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002259 ConsoleBuffer *cb,
2260 BOOL RestoreScreen)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002261{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002262 COORD BufferCoord;
2263 SMALL_RECT WriteRegion;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002264
2265 if (cb == NULL || !cb->IsValid)
2266 return FALSE;
2267
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002268 /*
2269 * Before restoring the buffer contents, clear the current buffer, and
2270 * restore the cursor position and window information. Doing this now
2271 * prevents old buffer contents from "flashing" onto the screen.
2272 */
2273 if (RestoreScreen)
2274 ClearConsoleBuffer(cb->Info.wAttributes);
2275
2276 FitConsoleWindow(cb->Info.dwSize, TRUE);
2277 if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
2278 return FALSE;
2279 if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
2280 return FALSE;
2281
2282 if (!RestoreScreen)
2283 {
2284 /*
2285 * No need to restore the screen buffer contents, so we're done.
2286 */
2287 return TRUE;
2288 }
2289
2290 if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
2291 return FALSE;
2292 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
2293 return FALSE;
2294
2295 /*
2296 * Restore the screen buffer contents.
2297 */
2298 if (cb->Buffer != NULL)
2299 {
2300 BufferCoord.X = 0;
2301 BufferCoord.Y = 0;
2302 WriteRegion.Left = 0;
2303 WriteRegion.Top = 0;
2304 WriteRegion.Right = cb->Info.dwSize.X - 1;
2305 WriteRegion.Bottom = cb->Info.dwSize.Y - 1;
2306 if (!WriteConsoleOutput(g_hConOut, /* output handle */
2307 cb->Buffer, /* our buffer */
2308 cb->BufferSize, /* dimensions of our buffer */
2309 BufferCoord, /* offset in our buffer */
2310 &WriteRegion)) /* region to restore */
2311 {
2312 return FALSE;
2313 }
2314 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002315
2316 return TRUE;
2317}
2318
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002319#define FEAT_RESTORE_ORIG_SCREEN
2320#ifdef FEAT_RESTORE_ORIG_SCREEN
2321static ConsoleBuffer g_cbOrig = { 0 };
2322#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002323static ConsoleBuffer g_cbNonTermcap = { 0 };
2324static ConsoleBuffer g_cbTermcap = { 0 };
2325
2326#ifdef FEAT_TITLE
2327#ifdef __BORLANDC__
2328typedef HWND (__stdcall *GETCONSOLEWINDOWPROC)(VOID);
2329#else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002330typedef HWND (WINAPI *GETCONSOLEWINDOWPROC)(VOID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002331#endif
2332char g_szOrigTitle[256] = { 0 };
2333HWND g_hWnd = NULL; /* also used in os_mswin.c */
2334static HICON g_hOrigIconSmall = NULL;
2335static HICON g_hOrigIcon = NULL;
2336static HICON g_hVimIcon = NULL;
2337static BOOL g_fCanChangeIcon = FALSE;
2338
2339/* ICON* are not defined in VC++ 4.0 */
2340#ifndef ICON_SMALL
2341#define ICON_SMALL 0
2342#endif
2343#ifndef ICON_BIG
2344#define ICON_BIG 1
2345#endif
2346/*
2347 * GetConsoleIcon()
2348 * Description:
2349 * Attempts to retrieve the small icon and/or the big icon currently in
2350 * use by a given window.
2351 * Returns:
2352 * TRUE on success
2353 */
2354 static BOOL
2355GetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002356 HWND hWnd,
2357 HICON *phIconSmall,
2358 HICON *phIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002359{
2360 if (hWnd == NULL)
2361 return FALSE;
2362
2363 if (phIconSmall != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002364 *phIconSmall = (HICON)SendMessage(hWnd, WM_GETICON,
2365 (WPARAM)ICON_SMALL, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002366 if (phIcon != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002367 *phIcon = (HICON)SendMessage(hWnd, WM_GETICON,
2368 (WPARAM)ICON_BIG, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002369 return TRUE;
2370}
2371
2372/*
2373 * SetConsoleIcon()
2374 * Description:
2375 * Attempts to change the small icon and/or the big icon currently in
2376 * use by a given window.
2377 * Returns:
2378 * TRUE on success
2379 */
2380 static BOOL
2381SetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002382 HWND hWnd,
2383 HICON hIconSmall,
2384 HICON hIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002385{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002386 if (hWnd == NULL)
2387 return FALSE;
2388
2389 if (hIconSmall != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002390 SendMessage(hWnd, WM_SETICON,
2391 (WPARAM)ICON_SMALL, (LPARAM)hIconSmall);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002392 if (hIcon != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002393 SendMessage(hWnd, WM_SETICON,
2394 (WPARAM)ICON_BIG, (LPARAM) hIcon);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002395 return TRUE;
2396}
2397
2398/*
2399 * SaveConsoleTitleAndIcon()
2400 * Description:
2401 * Saves the current console window title in g_szOrigTitle, for later
2402 * restoration. Also, attempts to obtain a handle to the console window,
2403 * and use it to save the small and big icons currently in use by the
2404 * console window. This is not always possible on some versions of Windows;
2405 * nor is it possible when running Vim remotely using Telnet (since the
2406 * console window the user sees is owned by a remote process).
2407 */
2408 static void
2409SaveConsoleTitleAndIcon(void)
2410{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002411 /* Save the original title. */
2412 if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle)))
2413 return;
2414
2415 /*
2416 * Obtain a handle to the console window using GetConsoleWindow() from
2417 * KERNEL32.DLL; we need to handle in order to change the window icon.
2418 * This function only exists on NT-based Windows, starting with Windows
2419 * 2000. On older operating systems, we can't change the window icon
2420 * anyway.
2421 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002422 g_hWnd = GetConsoleWindow();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002423 if (g_hWnd == NULL)
2424 return;
2425
2426 /* Save the original console window icon. */
2427 GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon);
2428 if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL)
2429 return;
2430
2431 /* Extract the first icon contained in the Vim executable. */
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02002432 if (mch_icon_load((HANDLE *)&g_hVimIcon) == FAIL || g_hVimIcon == NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002433 g_hVimIcon = ExtractIcon(NULL, (LPCSTR)exe_name, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002434 if (g_hVimIcon != NULL)
2435 g_fCanChangeIcon = TRUE;
2436}
2437#endif
2438
2439static int g_fWindInitCalled = FALSE;
2440static int g_fTermcapMode = FALSE;
2441static CONSOLE_CURSOR_INFO g_cci;
2442static DWORD g_cmodein = 0;
2443static DWORD g_cmodeout = 0;
2444
2445/*
2446 * non-GUI version of mch_init().
2447 */
2448 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002449mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002450{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002451#ifndef FEAT_RESTORE_ORIG_SCREEN
2452 CONSOLE_SCREEN_BUFFER_INFO csbi;
2453#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002454#ifndef __MINGW32__
2455 extern int _fmode;
2456#endif
2457
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002458 /* Silently handle invalid parameters to CRT functions */
2459 SET_INVALID_PARAM_HANDLER;
2460
Bram Moolenaar071d4272004-06-13 20:20:40 +00002461 /* Let critical errors result in a failure, not in a dialog box. Required
2462 * for the timestamp test to work on removed floppies. */
2463 SetErrorMode(SEM_FAILCRITICALERRORS);
2464
2465 _fmode = O_BINARY; /* we do our own CR-LF translation */
2466 out_flush();
2467
2468 /* Obtain handles for the standard Console I/O devices */
2469 if (read_cmd_fd == 0)
2470 g_hConIn = GetStdHandle(STD_INPUT_HANDLE);
2471 else
2472 create_conin();
2473 g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
2474
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002475#ifdef FEAT_RESTORE_ORIG_SCREEN
2476 /* Save the initial console buffer for later restoration */
2477 SaveConsoleBuffer(&g_cbOrig);
2478 g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
2479#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002480 /* Get current text attributes */
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01002481 GetConsoleScreenBufferInfo(g_hConOut, &csbi);
2482 g_attrCurrent = g_attrDefault = csbi.wAttributes;
2483#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484 if (cterm_normal_fg_color == 0)
2485 cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
2486 if (cterm_normal_bg_color == 0)
2487 cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
2488
2489 /* set termcap codes to current text attributes */
2490 update_tcap(g_attrCurrent);
2491
2492 GetConsoleCursorInfo(g_hConOut, &g_cci);
2493 GetConsoleMode(g_hConIn, &g_cmodein);
2494 GetConsoleMode(g_hConOut, &g_cmodeout);
2495
2496#ifdef FEAT_TITLE
2497 SaveConsoleTitleAndIcon();
2498 /*
2499 * Set both the small and big icons of the console window to Vim's icon.
2500 * Note that Vim presently only has one size of icon (32x32), but it
2501 * automatically gets scaled down to 16x16 when setting the small icon.
2502 */
2503 if (g_fCanChangeIcon)
2504 SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon);
2505#endif
2506
2507 ui_get_shellsize();
2508
2509#ifdef MCH_WRITE_DUMP
2510 fdDump = fopen("dump", "wt");
2511
2512 if (fdDump)
2513 {
2514 time_t t;
2515
2516 time(&t);
2517 fputs(ctime(&t), fdDump);
2518 fflush(fdDump);
2519 }
2520#endif
2521
2522 g_fWindInitCalled = TRUE;
2523
2524#ifdef FEAT_MOUSE
2525 g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
2526#endif
2527
2528#ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002529 win_clip_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002530#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002531}
2532
2533/*
2534 * non-GUI version of mch_exit().
2535 * Shut down and exit with status `r'
2536 * Careful: mch_exit() may be called before mch_init()!
2537 */
2538 void
2539mch_exit(int r)
2540{
Bram Moolenaar955f1982017-02-05 15:10:51 +01002541 exiting = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002542
Bram Moolenaar955f1982017-02-05 15:10:51 +01002543 stoptermcap();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002544 if (g_fWindInitCalled)
2545 settmode(TMODE_COOK);
2546
2547 ml_close_all(TRUE); /* remove all memfiles */
2548
2549 if (g_fWindInitCalled)
2550 {
2551#ifdef FEAT_TITLE
2552 mch_restore_title(3);
2553 /*
2554 * Restore both the small and big icons of the console window to
2555 * what they were at startup. Don't do this when the window is
2556 * closed, Vim would hang here.
2557 */
2558 if (g_fCanChangeIcon && !g_fForceExit)
2559 SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon);
2560#endif
2561
2562#ifdef MCH_WRITE_DUMP
2563 if (fdDump)
2564 {
2565 time_t t;
2566
2567 time(&t);
2568 fputs(ctime(&t), fdDump);
2569 fclose(fdDump);
2570 }
2571 fdDump = NULL;
2572#endif
2573 }
2574
2575 SetConsoleCursorInfo(g_hConOut, &g_cci);
2576 SetConsoleMode(g_hConIn, g_cmodein);
2577 SetConsoleMode(g_hConOut, g_cmodeout);
2578
2579#ifdef DYNAMIC_GETTEXT
2580 dyn_libintl_end();
2581#endif
2582
2583 exit(r);
2584}
2585#endif /* !FEAT_GUI_W32 */
2586
Bram Moolenaar071d4272004-06-13 20:20:40 +00002587/*
2588 * Do we have an interactive window?
2589 */
2590 int
2591mch_check_win(
Bram Moolenaar1266d672017-02-01 13:43:36 +01002592 int argc UNUSED,
2593 char **argv UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002594{
2595 get_exe_name();
2596
2597#ifdef FEAT_GUI_W32
2598 return OK; /* GUI always has a tty */
2599#else
2600 if (isatty(1))
2601 return OK;
2602 return FAIL;
2603#endif
2604}
2605
2606
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002607#ifdef FEAT_MBYTE
2608/*
2609 * fname_casew(): Wide version of fname_case(). Set the case of the file name,
2610 * if it already exists. When "len" is > 0, also expand short to long
2611 * filenames.
2612 * Return FAIL if wide functions are not available, OK otherwise.
2613 * NOTE: much of this is identical to fname_case(), keep in sync!
2614 */
2615 static int
2616fname_casew(
2617 WCHAR *name,
2618 int len)
2619{
2620 WCHAR szTrueName[_MAX_PATH + 2];
2621 WCHAR szTrueNameTemp[_MAX_PATH + 2];
2622 WCHAR *ptrue, *ptruePrev;
2623 WCHAR *porig, *porigPrev;
2624 int flen;
2625 WIN32_FIND_DATAW fb;
Bram Moolenaar73c61632013-12-07 14:48:10 +01002626 HANDLE hFind = INVALID_HANDLE_VALUE;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002627 int c;
2628 int slen;
2629
2630 flen = (int)wcslen(name);
2631 if (flen > _MAX_PATH)
2632 return OK;
2633
2634 /* slash_adjust(name) not needed, already adjusted by fname_case(). */
2635
2636 /* Build the new name in szTrueName[] one component at a time. */
2637 porig = name;
2638 ptrue = szTrueName;
2639
2640 if (iswalpha(porig[0]) && porig[1] == L':')
2641 {
2642 /* copy leading drive letter */
2643 *ptrue++ = *porig++;
2644 *ptrue++ = *porig++;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002645 }
Bram Moolenaar73c61632013-12-07 14:48:10 +01002646 *ptrue = NUL; /* in case nothing follows */
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002647
2648 while (*porig != NUL)
2649 {
2650 /* copy \ characters */
2651 while (*porig == psepc)
2652 *ptrue++ = *porig++;
2653
2654 ptruePrev = ptrue;
2655 porigPrev = porig;
2656 while (*porig != NUL && *porig != psepc)
2657 {
2658 *ptrue++ = *porig++;
2659 }
2660 *ptrue = NUL;
2661
2662 /* To avoid a slow failure append "\*" when searching a directory,
2663 * server or network share. */
2664 wcscpy(szTrueNameTemp, szTrueName);
2665 slen = (int)wcslen(szTrueNameTemp);
2666 if (*porig == psepc && slen + 2 < _MAX_PATH)
2667 wcscpy(szTrueNameTemp + slen, L"\\*");
2668
2669 /* Skip "", "." and "..". */
2670 if (ptrue > ptruePrev
2671 && (ptruePrev[0] != L'.'
2672 || (ptruePrev[1] != NUL
2673 && (ptruePrev[1] != L'.' || ptruePrev[2] != NUL)))
2674 && (hFind = FindFirstFileW(szTrueNameTemp, &fb))
2675 != INVALID_HANDLE_VALUE)
2676 {
2677 c = *porig;
2678 *porig = NUL;
2679
2680 /* Only use the match when it's the same name (ignoring case) or
2681 * expansion is allowed and there is a match with the short name
2682 * and there is enough room. */
2683 if (_wcsicoll(porigPrev, fb.cFileName) == 0
2684 || (len > 0
2685 && (_wcsicoll(porigPrev, fb.cAlternateFileName) == 0
2686 && (int)(ptruePrev - szTrueName)
2687 + (int)wcslen(fb.cFileName) < len)))
2688 {
2689 wcscpy(ptruePrev, fb.cFileName);
2690
2691 /* Look for exact match and prefer it if found. Must be a
2692 * long name, otherwise there would be only one match. */
2693 while (FindNextFileW(hFind, &fb))
2694 {
2695 if (*fb.cAlternateFileName != NUL
2696 && (wcscoll(porigPrev, fb.cFileName) == 0
2697 || (len > 0
2698 && (_wcsicoll(porigPrev,
2699 fb.cAlternateFileName) == 0
2700 && (int)(ptruePrev - szTrueName)
2701 + (int)wcslen(fb.cFileName) < len))))
2702 {
2703 wcscpy(ptruePrev, fb.cFileName);
2704 break;
2705 }
2706 }
2707 }
2708 FindClose(hFind);
2709 *porig = c;
2710 ptrue = ptruePrev + wcslen(ptruePrev);
2711 }
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002712 }
2713
2714 wcscpy(name, szTrueName);
2715 return OK;
2716}
2717#endif
2718
Bram Moolenaar071d4272004-06-13 20:20:40 +00002719/*
2720 * fname_case(): Set the case of the file name, if it already exists.
2721 * When "len" is > 0, also expand short to long filenames.
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002722 * NOTE: much of this is identical to fname_casew(), keep in sync!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723 */
2724 void
2725fname_case(
2726 char_u *name,
2727 int len)
2728{
2729 char szTrueName[_MAX_PATH + 2];
Bram Moolenaar464c9252010-10-13 20:37:41 +02002730 char szTrueNameTemp[_MAX_PATH + 2];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002731 char *ptrue, *ptruePrev;
2732 char *porig, *porigPrev;
2733 int flen;
2734 WIN32_FIND_DATA fb;
2735 HANDLE hFind;
2736 int c;
Bram Moolenaar464c9252010-10-13 20:37:41 +02002737 int slen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002738
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +00002739 flen = (int)STRLEN(name);
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002740 if (flen == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741 return;
2742
2743 slash_adjust(name);
2744
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002745#ifdef FEAT_MBYTE
2746 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2747 {
2748 WCHAR *p = enc_to_utf16(name, NULL);
2749
2750 if (p != NULL)
2751 {
2752 char_u *q;
Bram Moolenaar21d89b62014-10-07 10:38:40 +02002753 WCHAR buf[_MAX_PATH + 1];
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002754
Bram Moolenaar21d89b62014-10-07 10:38:40 +02002755 wcsncpy(buf, p, _MAX_PATH);
2756 buf[_MAX_PATH] = L'\0';
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002757 vim_free(p);
2758
2759 if (fname_casew(buf, (len > 0) ? _MAX_PATH : 0) == OK)
2760 {
2761 q = utf16_to_enc(buf, NULL);
2762 if (q != NULL)
2763 {
2764 vim_strncpy(name, q, (len > 0) ? len - 1 : flen);
2765 vim_free(q);
2766 return;
2767 }
2768 }
2769 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002770 return;
Bram Moolenaar65f04f62013-08-30 17:29:16 +02002771 }
2772#endif
2773
2774 /* If 'enc' is utf-8, flen can be larger than _MAX_PATH.
2775 * So we should check this after calling wide function. */
2776 if (flen > _MAX_PATH)
2777 return;
2778
Bram Moolenaar071d4272004-06-13 20:20:40 +00002779 /* Build the new name in szTrueName[] one component at a time. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002780 porig = (char *)name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002781 ptrue = szTrueName;
2782
2783 if (isalpha(porig[0]) && porig[1] == ':')
2784 {
2785 /* copy leading drive letter */
2786 *ptrue++ = *porig++;
2787 *ptrue++ = *porig++;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002788 }
Bram Moolenaar73c61632013-12-07 14:48:10 +01002789 *ptrue = NUL; /* in case nothing follows */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002790
2791 while (*porig != NUL)
2792 {
2793 /* copy \ characters */
2794 while (*porig == psepc)
2795 *ptrue++ = *porig++;
2796
2797 ptruePrev = ptrue;
2798 porigPrev = porig;
2799 while (*porig != NUL && *porig != psepc)
2800 {
2801#ifdef FEAT_MBYTE
2802 int l;
2803
2804 if (enc_dbcs)
2805 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002806 l = (*mb_ptr2len)((char_u *)porig);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002807 while (--l >= 0)
2808 *ptrue++ = *porig++;
2809 }
2810 else
2811#endif
2812 *ptrue++ = *porig++;
2813 }
2814 *ptrue = NUL;
2815
Bram Moolenaar464c9252010-10-13 20:37:41 +02002816 /* To avoid a slow failure append "\*" when searching a directory,
2817 * server or network share. */
2818 STRCPY(szTrueNameTemp, szTrueName);
Bram Moolenaar6b5ef062010-10-27 12:18:00 +02002819 slen = (int)strlen(szTrueNameTemp);
Bram Moolenaar464c9252010-10-13 20:37:41 +02002820 if (*porig == psepc && slen + 2 < _MAX_PATH)
2821 STRCPY(szTrueNameTemp + slen, "\\*");
2822
Bram Moolenaar071d4272004-06-13 20:20:40 +00002823 /* Skip "", "." and "..". */
2824 if (ptrue > ptruePrev
2825 && (ptruePrev[0] != '.'
2826 || (ptruePrev[1] != NUL
2827 && (ptruePrev[1] != '.' || ptruePrev[2] != NUL)))
Bram Moolenaar464c9252010-10-13 20:37:41 +02002828 && (hFind = FindFirstFile(szTrueNameTemp, &fb))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002829 != INVALID_HANDLE_VALUE)
2830 {
2831 c = *porig;
2832 *porig = NUL;
2833
2834 /* Only use the match when it's the same name (ignoring case) or
2835 * expansion is allowed and there is a match with the short name
2836 * and there is enough room. */
2837 if (_stricoll(porigPrev, fb.cFileName) == 0
2838 || (len > 0
2839 && (_stricoll(porigPrev, fb.cAlternateFileName) == 0
2840 && (int)(ptruePrev - szTrueName)
2841 + (int)strlen(fb.cFileName) < len)))
2842 {
2843 STRCPY(ptruePrev, fb.cFileName);
2844
2845 /* Look for exact match and prefer it if found. Must be a
2846 * long name, otherwise there would be only one match. */
2847 while (FindNextFile(hFind, &fb))
2848 {
2849 if (*fb.cAlternateFileName != NUL
2850 && (strcoll(porigPrev, fb.cFileName) == 0
2851 || (len > 0
2852 && (_stricoll(porigPrev,
2853 fb.cAlternateFileName) == 0
2854 && (int)(ptruePrev - szTrueName)
2855 + (int)strlen(fb.cFileName) < len))))
2856 {
2857 STRCPY(ptruePrev, fb.cFileName);
2858 break;
2859 }
2860 }
2861 }
2862 FindClose(hFind);
2863 *porig = c;
2864 ptrue = ptruePrev + strlen(ptruePrev);
2865 }
2866 }
2867
2868 STRCPY(name, szTrueName);
2869}
2870
2871
2872/*
2873 * Insert user name in s[len].
2874 */
2875 int
2876mch_get_user_name(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002877 char_u *s,
2878 int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002879{
Bram Moolenaar41a09032007-10-01 18:34:34 +00002880 char szUserName[256 + 1]; /* UNLEN is 256 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002881 DWORD cch = sizeof szUserName;
2882
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01002883#ifdef FEAT_MBYTE
2884 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2885 {
2886 WCHAR wszUserName[256 + 1]; /* UNLEN is 256 */
2887 DWORD wcch = sizeof(wszUserName) / sizeof(WCHAR);
2888
2889 if (GetUserNameW(wszUserName, &wcch))
2890 {
2891 char_u *p = utf16_to_enc(wszUserName, NULL);
2892
2893 if (p != NULL)
2894 {
2895 vim_strncpy(s, p, len - 1);
2896 vim_free(p);
2897 return OK;
2898 }
2899 }
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01002900 }
2901#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002902 if (GetUserName(szUserName, &cch))
2903 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002904 vim_strncpy(s, (char_u *)szUserName, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002905 return OK;
2906 }
2907 s[0] = NUL;
2908 return FAIL;
2909}
2910
2911
2912/*
2913 * Insert host name in s[len].
2914 */
2915 void
2916mch_get_host_name(
2917 char_u *s,
2918 int len)
2919{
2920 DWORD cch = len;
2921
Bram Moolenaar2cc87382013-12-11 18:21:45 +01002922#ifdef FEAT_MBYTE
2923 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2924 {
2925 WCHAR wszHostName[256 + 1];
2926 DWORD wcch = sizeof(wszHostName) / sizeof(WCHAR);
2927
2928 if (GetComputerNameW(wszHostName, &wcch))
2929 {
2930 char_u *p = utf16_to_enc(wszHostName, NULL);
2931
2932 if (p != NULL)
2933 {
2934 vim_strncpy(s, p, len - 1);
2935 vim_free(p);
2936 return;
2937 }
2938 }
Bram Moolenaar2cc87382013-12-11 18:21:45 +01002939 }
2940#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002941 if (!GetComputerName((LPSTR)s, &cch))
2942 vim_strncpy(s, (char_u *)"PC (Win32 Vim)", len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002943}
2944
2945
2946/*
2947 * return process ID
2948 */
2949 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002950mch_get_pid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002951{
2952 return (long)GetCurrentProcessId();
2953}
2954
2955
2956/*
2957 * Get name of current directory into buffer 'buf' of length 'len' bytes.
2958 * Return OK for success, FAIL for failure.
2959 */
2960 int
2961mch_dirname(
2962 char_u *buf,
2963 int len)
2964{
2965 /*
2966 * Originally this was:
2967 * return (getcwd(buf, len) != NULL ? OK : FAIL);
2968 * But the Win32s known bug list says that getcwd() doesn't work
2969 * so use the Win32 system call instead. <Negri>
2970 */
2971#ifdef FEAT_MBYTE
2972 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2973 {
2974 WCHAR wbuf[_MAX_PATH + 1];
2975
2976 if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0)
2977 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00002978 char_u *p = utf16_to_enc(wbuf, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002979
2980 if (p != NULL)
2981 {
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00002982 vim_strncpy(buf, p, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002983 vim_free(p);
2984 return OK;
2985 }
2986 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002987 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002988 }
2989#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002990 return (GetCurrentDirectory(len, (LPSTR)buf) != 0 ? OK : FAIL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002991}
2992
2993/*
Bram Moolenaarffa22202013-11-21 12:34:11 +01002994 * Get file permissions for "name".
2995 * Return mode_t or -1 for error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002996 */
2997 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002998mch_getperm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002999{
Bram Moolenaar8767f522016-07-01 17:17:39 +02003000 stat_T st;
Bram Moolenaarffa22202013-11-21 12:34:11 +01003001 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003002
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003003 n = mch_stat((char *)name, &st);
Bram Moolenaar78cf3f02014-01-10 18:16:07 +01003004 return n == 0 ? (long)(unsigned short)st.st_mode : -1L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003005}
3006
3007
3008/*
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003009 * Set file permission for "name" to "perm".
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003010 *
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003011 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003012 */
3013 int
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003014mch_setperm(char_u *name, long perm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003015{
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003016 long n = -1;
3017
Bram Moolenaar071d4272004-06-13 20:20:40 +00003018#ifdef FEAT_MBYTE
3019 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3020 {
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003021 WCHAR *p = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003022
3023 if (p != NULL)
3024 {
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003025 n = _wchmod(p, perm);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003026 vim_free(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003027 if (n == -1)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003028 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003029 }
3030 }
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003031 if (n == -1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003032#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003033 n = _chmod((const char *)name, perm);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003034 if (n == -1)
3035 return FAIL;
3036
3037 win32_set_archive(name);
3038
3039 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003040}
3041
3042/*
3043 * Set hidden flag for "name".
3044 */
3045 void
3046mch_hide(char_u *name)
3047{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003048 int attrs = win32_getattrs(name);
3049 if (attrs == -1)
3050 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003051
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003052 attrs |= FILE_ATTRIBUTE_HIDDEN;
3053 win32_setattrs(name, attrs);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003054}
3055
3056/*
Bram Moolenaar8a52ba72015-11-02 14:45:56 +01003057 * Return TRUE if file "name" exists and is hidden.
3058 */
3059 int
3060mch_ishidden(char_u *name)
3061{
3062 int f = win32_getattrs(name);
3063
3064 if (f == -1)
3065 return FALSE; /* file does not exist at all */
3066
3067 return (f & FILE_ATTRIBUTE_HIDDEN) != 0;
3068}
3069
3070/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003071 * return TRUE if "name" is a directory
3072 * return FALSE if "name" is not a directory or upon error
3073 */
3074 int
3075mch_isdir(char_u *name)
3076{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003077 int f = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003078
3079 if (f == -1)
3080 return FALSE; /* file does not exist at all */
3081
3082 return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
3083}
3084
3085/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003086 * return TRUE if "name" is a directory, NOT a symlink to a directory
3087 * return FALSE if "name" is not a directory
3088 * return FALSE for error
3089 */
3090 int
3091mch_isrealdir(char_u *name)
3092{
3093 return mch_isdir(name) && !mch_is_symbolic_link(name);
3094}
3095
3096/*
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003097 * Create directory "name".
3098 * Return 0 on success, -1 on error.
3099 */
3100 int
3101mch_mkdir(char_u *name)
3102{
3103#ifdef FEAT_MBYTE
3104 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3105 {
3106 WCHAR *p;
3107 int retval;
3108
3109 p = enc_to_utf16(name, NULL);
3110 if (p == NULL)
3111 return -1;
3112 retval = _wmkdir(p);
3113 vim_free(p);
3114 return retval;
3115 }
3116#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003117 return _mkdir((const char *)name);
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003118}
3119
3120/*
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003121 * Delete directory "name".
3122 * Return 0 on success, -1 on error.
3123 */
3124 int
3125mch_rmdir(char_u *name)
3126{
3127#ifdef FEAT_MBYTE
3128 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3129 {
3130 WCHAR *p;
3131 int retval;
3132
3133 p = enc_to_utf16(name, NULL);
3134 if (p == NULL)
3135 return -1;
3136 retval = _wrmdir(p);
3137 vim_free(p);
3138 return retval;
3139 }
3140#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003141 return _rmdir((const char *)name);
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003142}
3143
3144/*
Bram Moolenaar03f48552006-02-28 23:52:23 +00003145 * Return TRUE if file "fname" has more than one link.
3146 */
3147 int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003148mch_is_hard_link(char_u *fname)
Bram Moolenaar03f48552006-02-28 23:52:23 +00003149{
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003150 BY_HANDLE_FILE_INFORMATION info;
3151
3152 return win32_fileinfo(fname, &info) == FILEINFO_OK
3153 && info.nNumberOfLinks > 1;
3154}
3155
3156/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003157 * Return TRUE if "name" is a symbolic link (or a junction).
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003158 */
3159 int
Bram Moolenaar203258c2016-01-17 22:15:16 +01003160mch_is_symbolic_link(char_u *name)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003161{
3162 HANDLE hFind;
3163 int res = FALSE;
3164 WIN32_FIND_DATAA findDataA;
3165 DWORD fileFlags = 0, reparseTag = 0;
3166#ifdef FEAT_MBYTE
3167 WCHAR *wn = NULL;
3168 WIN32_FIND_DATAW findDataW;
3169
3170 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar203258c2016-01-17 22:15:16 +01003171 wn = enc_to_utf16(name, NULL);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003172 if (wn != NULL)
3173 {
3174 hFind = FindFirstFileW(wn, &findDataW);
3175 vim_free(wn);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003176 if (hFind != INVALID_HANDLE_VALUE)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003177 {
3178 fileFlags = findDataW.dwFileAttributes;
3179 reparseTag = findDataW.dwReserved0;
3180 }
3181 }
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003182 else
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003183#endif
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003184 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003185 hFind = FindFirstFile((LPCSTR)name, &findDataA);
Bram Moolenaar03e114b2013-06-16 16:34:56 +02003186 if (hFind != INVALID_HANDLE_VALUE)
3187 {
3188 fileFlags = findDataA.dwFileAttributes;
3189 reparseTag = findDataA.dwReserved0;
3190 }
3191 }
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003192
3193 if (hFind != INVALID_HANDLE_VALUE)
3194 FindClose(hFind);
3195
3196 if ((fileFlags & FILE_ATTRIBUTE_REPARSE_POINT)
Bram Moolenaar203258c2016-01-17 22:15:16 +01003197 && (reparseTag == IO_REPARSE_TAG_SYMLINK
3198 || reparseTag == IO_REPARSE_TAG_MOUNT_POINT))
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003199 res = TRUE;
3200
3201 return res;
3202}
3203
3204/*
3205 * Return TRUE if file "fname" has more than one link or if it is a symbolic
3206 * link.
3207 */
3208 int
3209mch_is_linked(char_u *fname)
3210{
3211 if (mch_is_hard_link(fname) || mch_is_symbolic_link(fname))
3212 return TRUE;
3213 return FALSE;
3214}
3215
3216/*
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003217 * Get the by-handle-file-information for "fname".
3218 * Returns FILEINFO_OK when OK.
3219 * returns FILEINFO_ENC_FAIL when enc_to_utf16() failed.
3220 * Returns FILEINFO_READ_FAIL when CreateFile() failed.
3221 * Returns FILEINFO_INFO_FAIL when GetFileInformationByHandle() failed.
3222 */
3223 int
3224win32_fileinfo(char_u *fname, BY_HANDLE_FILE_INFORMATION *info)
3225{
Bram Moolenaar03f48552006-02-28 23:52:23 +00003226 HANDLE hFile;
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003227 int res = FILEINFO_READ_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003228#ifdef FEAT_MBYTE
3229 WCHAR *wn = NULL;
3230
3231 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003232 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00003233 wn = enc_to_utf16(fname, NULL);
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003234 if (wn == NULL)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003235 return FILEINFO_ENC_FAIL;
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003236 }
Bram Moolenaar03f48552006-02-28 23:52:23 +00003237 if (wn != NULL)
3238 {
3239 hFile = CreateFileW(wn, /* file name */
3240 GENERIC_READ, /* access mode */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003241 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003242 NULL, /* security descriptor */
3243 OPEN_EXISTING, /* creation disposition */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003244 FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003245 NULL); /* handle to template file */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003246 vim_free(wn);
Bram Moolenaar03f48552006-02-28 23:52:23 +00003247 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003248 else
Bram Moolenaar03f48552006-02-28 23:52:23 +00003249#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003250 hFile = CreateFile((LPCSTR)fname, /* file name */
3251 GENERIC_READ, /* access mode */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003252 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003253 NULL, /* security descriptor */
3254 OPEN_EXISTING, /* creation disposition */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003255 FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
Bram Moolenaar03f48552006-02-28 23:52:23 +00003256 NULL); /* handle to template file */
3257
3258 if (hFile != INVALID_HANDLE_VALUE)
3259 {
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003260 if (GetFileInformationByHandle(hFile, info) != 0)
3261 res = FILEINFO_OK;
3262 else
3263 res = FILEINFO_INFO_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003264 CloseHandle(hFile);
3265 }
3266
Bram Moolenaar03f48552006-02-28 23:52:23 +00003267 return res;
3268}
3269
3270/*
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003271 * get file attributes for `name'
3272 * -1 : error
3273 * else FILE_ATTRIBUTE_* defined in winnt.h
3274 */
Bram Moolenaarffa22202013-11-21 12:34:11 +01003275 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003276win32_getattrs(char_u *name)
3277{
3278 int attr;
3279#ifdef FEAT_MBYTE
3280 WCHAR *p = NULL;
3281
3282 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3283 p = enc_to_utf16(name, NULL);
3284
3285 if (p != NULL)
3286 {
3287 attr = GetFileAttributesW(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003288 vim_free(p);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003289 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003290 else
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003291#endif
3292 attr = GetFileAttributes((char *)name);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003293
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003294 return attr;
3295}
3296
3297/*
3298 * set file attributes for `name' to `attrs'
3299 *
3300 * return -1 for failure, 0 otherwise
3301 */
3302 static
3303 int
3304win32_setattrs(char_u *name, int attrs)
3305{
3306 int res;
3307#ifdef FEAT_MBYTE
3308 WCHAR *p = NULL;
3309
3310 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3311 p = enc_to_utf16(name, NULL);
3312
3313 if (p != NULL)
3314 {
3315 res = SetFileAttributesW(p, attrs);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003316 vim_free(p);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003317 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003318 else
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003319#endif
3320 res = SetFileAttributes((char *)name, attrs);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003321
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003322 return res ? 0 : -1;
3323}
3324
3325/*
3326 * Set archive flag for "name".
3327 */
3328 static
3329 int
3330win32_set_archive(char_u *name)
3331{
3332 int attrs = win32_getattrs(name);
3333 if (attrs == -1)
3334 return -1;
3335
3336 attrs |= FILE_ATTRIBUTE_ARCHIVE;
3337 return win32_setattrs(name, attrs);
3338}
3339
3340/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003341 * Return TRUE if file or directory "name" is writable (not readonly).
3342 * Strange semantics of Win32: a readonly directory is writable, but you can't
3343 * delete a file. Let's say this means it is writable.
3344 */
3345 int
3346mch_writable(char_u *name)
3347{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003348 int attrs = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003349
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003350 return (attrs != -1 && (!(attrs & FILE_ATTRIBUTE_READONLY)
3351 || (attrs & FILE_ATTRIBUTE_DIRECTORY)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003352}
3353
Bram Moolenaar071d4272004-06-13 20:20:40 +00003354/*
3355 * Return 1 if "name" can be executed, 0 if not.
Bram Moolenaar77b77102015-03-21 22:18:41 +01003356 * If "use_path" is FALSE only check if "name" is executable.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357 * Return -1 if unknown.
3358 */
3359 int
Bram Moolenaar77b77102015-03-21 22:18:41 +01003360mch_can_exe(char_u *name, char_u **path, int use_path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003361{
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003362 char_u buf[_MAX_PATH];
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003363 int len = (int)STRLEN(name);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003364 char_u *p;
3365
3366 if (len >= _MAX_PATH) /* safety check */
3367 return FALSE;
Bram Moolenaar77b77102015-03-21 22:18:41 +01003368 if (!use_path)
3369 {
3370 /* TODO: check if file is really executable. */
3371 return mch_getperm(name) != -1 && !mch_isdir(name);
3372 }
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003373
3374 /* If there already is an extension try using the name directly. Also do
3375 * this with a Unix-shell like 'shell'. */
3376 if (vim_strchr(gettail(name), '.') != NULL
3377 || strstr((char *)gettail(p_sh), "sh") != NULL)
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003378 if (executable_exists((char *)name, path))
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003379 return TRUE;
3380
3381 /*
3382 * Loop over all extensions in $PATHEXT.
3383 */
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00003384 vim_strncpy(buf, name, _MAX_PATH - 1);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003385 p = mch_getenv("PATHEXT");
3386 if (p == NULL)
3387 p = (char_u *)".com;.exe;.bat;.cmd";
3388 while (*p)
3389 {
3390 if (p[0] == '.' && (p[1] == NUL || p[1] == ';'))
3391 {
3392 /* A single "." means no extension is added. */
3393 buf[len] = NUL;
3394 ++p;
3395 if (*p)
3396 ++p;
3397 }
3398 else
3399 copy_option_part(&p, buf + len, _MAX_PATH - len, ";");
Bram Moolenaarc7f02552014-04-01 21:00:59 +02003400 if (executable_exists((char *)buf, path))
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00003401 return TRUE;
3402 }
3403 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003404}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003405
3406/*
3407 * Check what "name" is:
3408 * NODE_NORMAL: file or directory (or doesn't exist)
3409 * NODE_WRITABLE: writable device, socket, fifo, etc.
3410 * NODE_OTHER: non-writable things
3411 */
3412 int
3413mch_nodetype(char_u *name)
3414{
3415 HANDLE hFile;
3416 int type;
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003417#ifdef FEAT_MBYTE
3418 WCHAR *wn = NULL;
3419#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003420
Bram Moolenaar043545e2006-10-10 16:44:07 +00003421 /* We can't open a file with a name "\\.\con" or "\\.\prn" and trying to
3422 * read from it later will cause Vim to hang. Thus return NODE_WRITABLE
3423 * here. */
3424 if (STRNCMP(name, "\\\\.\\", 4) == 0)
3425 return NODE_WRITABLE;
3426
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003427#ifdef FEAT_MBYTE
3428 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003429 wn = enc_to_utf16(name, NULL);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003430
3431 if (wn != NULL)
3432 {
3433 hFile = CreateFileW(wn, /* file name */
3434 GENERIC_WRITE, /* access mode */
3435 0, /* share mode */
3436 NULL, /* security descriptor */
3437 OPEN_EXISTING, /* creation disposition */
3438 0, /* file attributes */
3439 NULL); /* handle to template file */
3440 vim_free(wn);
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003441 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003442 else
Bram Moolenaar4dee1bb2013-08-30 17:11:33 +02003443#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003444 hFile = CreateFile((LPCSTR)name, /* file name */
3445 GENERIC_WRITE, /* access mode */
3446 0, /* share mode */
3447 NULL, /* security descriptor */
3448 OPEN_EXISTING, /* creation disposition */
3449 0, /* file attributes */
3450 NULL); /* handle to template file */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003451
3452 if (hFile == INVALID_HANDLE_VALUE)
3453 return NODE_NORMAL;
3454
3455 type = GetFileType(hFile);
3456 CloseHandle(hFile);
3457 if (type == FILE_TYPE_CHAR)
3458 return NODE_WRITABLE;
3459 if (type == FILE_TYPE_DISK)
3460 return NODE_NORMAL;
3461 return NODE_OTHER;
3462}
3463
3464#ifdef HAVE_ACL
3465struct my_acl
3466{
3467 PSECURITY_DESCRIPTOR pSecurityDescriptor;
3468 PSID pSidOwner;
3469 PSID pSidGroup;
3470 PACL pDacl;
3471 PACL pSacl;
3472};
3473#endif
3474
3475/*
3476 * Return a pointer to the ACL of file "fname" in allocated memory.
3477 * Return NULL if the ACL is not available for whatever reason.
3478 */
3479 vim_acl_T
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003480mch_get_acl(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003481{
3482#ifndef HAVE_ACL
3483 return (vim_acl_T)NULL;
3484#else
3485 struct my_acl *p = NULL;
Bram Moolenaar27515922013-06-29 15:36:26 +02003486 DWORD err;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003487
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003488 p = (struct my_acl *)alloc_clear((unsigned)sizeof(struct my_acl));
3489 if (p != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003490 {
Bram Moolenaar27515922013-06-29 15:36:26 +02003491# ifdef FEAT_MBYTE
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003492 WCHAR *wn = NULL;
Bram Moolenaar27515922013-06-29 15:36:26 +02003493
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003494 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3495 wn = enc_to_utf16(fname, NULL);
3496 if (wn != NULL)
3497 {
3498 /* Try to retrieve the entire security descriptor. */
3499 err = GetNamedSecurityInfoW(
3500 wn, // Abstract filename
3501 SE_FILE_OBJECT, // File Object
3502 OWNER_SECURITY_INFORMATION |
3503 GROUP_SECURITY_INFORMATION |
3504 DACL_SECURITY_INFORMATION |
3505 SACL_SECURITY_INFORMATION,
3506 &p->pSidOwner, // Ownership information.
3507 &p->pSidGroup, // Group membership.
3508 &p->pDacl, // Discretionary information.
3509 &p->pSacl, // For auditing purposes.
3510 &p->pSecurityDescriptor);
3511 if (err == ERROR_ACCESS_DENIED ||
3512 err == ERROR_PRIVILEGE_NOT_HELD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003513 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003514 /* Retrieve only DACL. */
3515 (void)GetNamedSecurityInfoW(
3516 wn,
3517 SE_FILE_OBJECT,
3518 DACL_SECURITY_INFORMATION,
3519 NULL,
3520 NULL,
3521 &p->pDacl,
3522 NULL,
3523 &p->pSecurityDescriptor);
Bram Moolenaar27515922013-06-29 15:36:26 +02003524 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003525 if (p->pSecurityDescriptor == NULL)
Bram Moolenaar27515922013-06-29 15:36:26 +02003526 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003527 mch_free_acl((vim_acl_T)p);
3528 p = NULL;
3529 }
3530 vim_free(wn);
3531 }
3532 else
3533# endif
3534 {
3535 /* Try to retrieve the entire security descriptor. */
3536 err = GetNamedSecurityInfo(
3537 (LPSTR)fname, // Abstract filename
3538 SE_FILE_OBJECT, // File Object
3539 OWNER_SECURITY_INFORMATION |
3540 GROUP_SECURITY_INFORMATION |
3541 DACL_SECURITY_INFORMATION |
3542 SACL_SECURITY_INFORMATION,
3543 &p->pSidOwner, // Ownership information.
3544 &p->pSidGroup, // Group membership.
3545 &p->pDacl, // Discretionary information.
3546 &p->pSacl, // For auditing purposes.
3547 &p->pSecurityDescriptor);
3548 if (err == ERROR_ACCESS_DENIED ||
3549 err == ERROR_PRIVILEGE_NOT_HELD)
3550 {
3551 /* Retrieve only DACL. */
3552 (void)GetNamedSecurityInfo(
3553 (LPSTR)fname,
3554 SE_FILE_OBJECT,
3555 DACL_SECURITY_INFORMATION,
3556 NULL,
3557 NULL,
3558 &p->pDacl,
3559 NULL,
3560 &p->pSecurityDescriptor);
3561 }
3562 if (p->pSecurityDescriptor == NULL)
3563 {
3564 mch_free_acl((vim_acl_T)p);
3565 p = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003566 }
3567 }
3568 }
3569
3570 return (vim_acl_T)p;
3571#endif
3572}
3573
Bram Moolenaar27515922013-06-29 15:36:26 +02003574#ifdef HAVE_ACL
3575/*
3576 * Check if "acl" contains inherited ACE.
3577 */
3578 static BOOL
3579is_acl_inherited(PACL acl)
3580{
3581 DWORD i;
3582 ACL_SIZE_INFORMATION acl_info;
3583 PACCESS_ALLOWED_ACE ace;
3584
3585 acl_info.AceCount = 0;
3586 GetAclInformation(acl, &acl_info, sizeof(acl_info), AclSizeInformation);
3587 for (i = 0; i < acl_info.AceCount; i++)
3588 {
3589 GetAce(acl, i, (LPVOID *)&ace);
3590 if (ace->Header.AceFlags & INHERITED_ACE)
3591 return TRUE;
3592 }
3593 return FALSE;
3594}
3595#endif
3596
Bram Moolenaar071d4272004-06-13 20:20:40 +00003597/*
3598 * Set the ACL of file "fname" to "acl" (unless it's NULL).
3599 * Errors are ignored.
3600 * This must only be called with "acl" equal to what mch_get_acl() returned.
3601 */
3602 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003603mch_set_acl(char_u *fname, vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003604{
3605#ifdef HAVE_ACL
3606 struct my_acl *p = (struct my_acl *)acl;
Bram Moolenaar27515922013-06-29 15:36:26 +02003607 SECURITY_INFORMATION sec_info = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003609 if (p != NULL)
Bram Moolenaar27515922013-06-29 15:36:26 +02003610 {
3611# ifdef FEAT_MBYTE
3612 WCHAR *wn = NULL;
3613# endif
3614
3615 /* Set security flags */
3616 if (p->pSidOwner)
3617 sec_info |= OWNER_SECURITY_INFORMATION;
3618 if (p->pSidGroup)
3619 sec_info |= GROUP_SECURITY_INFORMATION;
3620 if (p->pDacl)
3621 {
3622 sec_info |= DACL_SECURITY_INFORMATION;
3623 /* Do not inherit its parent's DACL.
3624 * If the DACL is inherited, Cygwin permissions would be changed.
3625 */
3626 if (!is_acl_inherited(p->pDacl))
3627 sec_info |= PROTECTED_DACL_SECURITY_INFORMATION;
3628 }
3629 if (p->pSacl)
3630 sec_info |= SACL_SECURITY_INFORMATION;
3631
3632# ifdef FEAT_MBYTE
3633 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3634 wn = enc_to_utf16(fname, NULL);
3635 if (wn != NULL)
3636 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003637 (void)SetNamedSecurityInfoW(
Bram Moolenaar27515922013-06-29 15:36:26 +02003638 wn, // Abstract filename
3639 SE_FILE_OBJECT, // File Object
3640 sec_info,
3641 p->pSidOwner, // Ownership information.
3642 p->pSidGroup, // Group membership.
3643 p->pDacl, // Discretionary information.
3644 p->pSacl // For auditing purposes.
3645 );
3646 vim_free(wn);
3647 }
3648 else
3649# endif
3650 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003651 (void)SetNamedSecurityInfo(
Bram Moolenaar27515922013-06-29 15:36:26 +02003652 (LPSTR)fname, // Abstract filename
3653 SE_FILE_OBJECT, // File Object
3654 sec_info,
3655 p->pSidOwner, // Ownership information.
3656 p->pSidGroup, // Group membership.
3657 p->pDacl, // Discretionary information.
3658 p->pSacl // For auditing purposes.
3659 );
3660 }
3661 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662#endif
3663}
3664
3665 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003666mch_free_acl(vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003667{
3668#ifdef HAVE_ACL
3669 struct my_acl *p = (struct my_acl *)acl;
3670
3671 if (p != NULL)
3672 {
3673 LocalFree(p->pSecurityDescriptor); // Free the memory just in case
3674 vim_free(p);
3675 }
3676#endif
3677}
3678
3679#ifndef FEAT_GUI_W32
3680
3681/*
3682 * handler for ctrl-break, ctrl-c interrupts, and fatal events.
3683 */
3684 static BOOL WINAPI
3685handler_routine(
3686 DWORD dwCtrlType)
3687{
3688 switch (dwCtrlType)
3689 {
3690 case CTRL_C_EVENT:
3691 if (ctrl_c_interrupts)
3692 g_fCtrlCPressed = TRUE;
3693 return TRUE;
3694
3695 case CTRL_BREAK_EVENT:
3696 g_fCBrkPressed = TRUE;
3697 return TRUE;
3698
3699 /* fatal events: shut down gracefully */
3700 case CTRL_CLOSE_EVENT:
3701 case CTRL_LOGOFF_EVENT:
3702 case CTRL_SHUTDOWN_EVENT:
3703 windgoto((int)Rows - 1, 0);
3704 g_fForceExit = TRUE;
3705
Bram Moolenaar0fde2902008-03-16 13:54:13 +00003706 vim_snprintf((char *)IObuff, IOSIZE, _("Vim: Caught %s event\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003707 (dwCtrlType == CTRL_CLOSE_EVENT
3708 ? _("close")
3709 : dwCtrlType == CTRL_LOGOFF_EVENT
3710 ? _("logoff")
3711 : _("shutdown")));
3712#ifdef DEBUG
3713 OutputDebugString(IObuff);
3714#endif
3715
3716 preserve_exit(); /* output IObuff, preserve files and exit */
3717
3718 return TRUE; /* not reached */
3719
3720 default:
3721 return FALSE;
3722 }
3723}
3724
3725
3726/*
3727 * set the tty in (raw) ? "raw" : "cooked" mode
3728 */
3729 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003730mch_settmode(int tmode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003731{
3732 DWORD cmodein;
3733 DWORD cmodeout;
3734 BOOL bEnableHandler;
3735
3736 GetConsoleMode(g_hConIn, &cmodein);
3737 GetConsoleMode(g_hConOut, &cmodeout);
3738 if (tmode == TMODE_RAW)
3739 {
3740 cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3741 ENABLE_ECHO_INPUT);
3742#ifdef FEAT_MOUSE
3743 if (g_fMouseActive)
3744 cmodein |= ENABLE_MOUSE_INPUT;
3745#endif
3746 cmodeout &= ~(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3747 bEnableHandler = TRUE;
3748 }
3749 else /* cooked */
3750 {
3751 cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3752 ENABLE_ECHO_INPUT);
3753 cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3754 bEnableHandler = FALSE;
3755 }
3756 SetConsoleMode(g_hConIn, cmodein);
3757 SetConsoleMode(g_hConOut, cmodeout);
3758 SetConsoleCtrlHandler(handler_routine, bEnableHandler);
3759
3760#ifdef MCH_WRITE_DUMP
3761 if (fdDump)
3762 {
3763 fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n",
3764 tmode == TMODE_RAW ? "raw" :
3765 tmode == TMODE_COOK ? "cooked" : "normal",
3766 cmodein, cmodeout);
3767 fflush(fdDump);
3768 }
3769#endif
3770}
3771
3772
3773/*
3774 * Get the size of the current window in `Rows' and `Columns'
3775 * Return OK when size could be determined, FAIL otherwise.
3776 */
3777 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003778mch_get_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003779{
3780 CONSOLE_SCREEN_BUFFER_INFO csbi;
3781
3782 if (!g_fTermcapMode && g_cbTermcap.IsValid)
3783 {
3784 /*
3785 * For some reason, we are trying to get the screen dimensions
3786 * even though we are not in termcap mode. The 'Rows' and 'Columns'
3787 * variables are really intended to mean the size of Vim screen
3788 * while in termcap mode.
3789 */
3790 Rows = g_cbTermcap.Info.dwSize.Y;
3791 Columns = g_cbTermcap.Info.dwSize.X;
3792 }
3793 else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3794 {
3795 Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3796 Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3797 }
3798 else
3799 {
3800 Rows = 25;
3801 Columns = 80;
3802 }
3803 return OK;
3804}
3805
3806/*
3807 * Set a console window to `xSize' * `ySize'
3808 */
3809 static void
3810ResizeConBufAndWindow(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003811 HANDLE hConsole,
3812 int xSize,
3813 int ySize)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003814{
3815 CONSOLE_SCREEN_BUFFER_INFO csbi; /* hold current console buffer info */
3816 SMALL_RECT srWindowRect; /* hold the new console size */
3817 COORD coordScreen;
3818
3819#ifdef MCH_WRITE_DUMP
3820 if (fdDump)
3821 {
3822 fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
3823 fflush(fdDump);
3824 }
3825#endif
3826
3827 /* get the largest size we can size the console window to */
3828 coordScreen = GetLargestConsoleWindowSize(hConsole);
3829
3830 /* define the new console window size and scroll position */
3831 srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
3832 srWindowRect.Right = (SHORT) (min(xSize, coordScreen.X) - 1);
3833 srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
3834
3835 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3836 {
3837 int sx, sy;
3838
3839 sx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3840 sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3841 if (sy < ySize || sx < xSize)
3842 {
3843 /*
3844 * Increasing number of lines/columns, do buffer first.
3845 * Use the maximal size in x and y direction.
3846 */
3847 if (sy < ySize)
3848 coordScreen.Y = ySize;
3849 else
3850 coordScreen.Y = sy;
3851 if (sx < xSize)
3852 coordScreen.X = xSize;
3853 else
3854 coordScreen.X = sx;
3855 SetConsoleScreenBufferSize(hConsole, coordScreen);
3856 }
3857 }
3858
3859 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &srWindowRect))
3860 {
3861#ifdef MCH_WRITE_DUMP
3862 if (fdDump)
3863 {
3864 fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
3865 GetLastError());
3866 fflush(fdDump);
3867 }
3868#endif
3869 }
3870
3871 /* define the new console buffer size */
3872 coordScreen.X = xSize;
3873 coordScreen.Y = ySize;
3874
3875 if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
3876 {
3877#ifdef MCH_WRITE_DUMP
3878 if (fdDump)
3879 {
3880 fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
3881 GetLastError());
3882 fflush(fdDump);
3883 }
3884#endif
3885 }
3886}
3887
3888
3889/*
3890 * Set the console window to `Rows' * `Columns'
3891 */
3892 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003893mch_set_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003894{
3895 COORD coordScreen;
3896
3897 /* Don't change window size while still starting up */
3898 if (suppress_winsize != 0)
3899 {
3900 suppress_winsize = 2;
3901 return;
3902 }
3903
3904 if (term_console)
3905 {
3906 coordScreen = GetLargestConsoleWindowSize(g_hConOut);
3907
3908 /* Clamp Rows and Columns to reasonable values */
3909 if (Rows > coordScreen.Y)
3910 Rows = coordScreen.Y;
3911 if (Columns > coordScreen.X)
3912 Columns = coordScreen.X;
3913
3914 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
3915 }
3916}
3917
3918/*
3919 * Rows and/or Columns has changed.
3920 */
3921 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003922mch_new_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003923{
3924 set_scroll_region(0, 0, Columns - 1, Rows - 1);
3925}
3926
3927
3928/*
3929 * Called when started up, to set the winsize that was delayed.
3930 */
3931 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003932mch_set_winsize_now(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003933{
3934 if (suppress_winsize == 2)
3935 {
3936 suppress_winsize = 0;
3937 mch_set_shellsize();
3938 shell_resized();
3939 }
3940 suppress_winsize = 0;
3941}
3942#endif /* FEAT_GUI_W32 */
3943
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003944 static BOOL
3945vim_create_process(
Bram Moolenaar36c85b22013-12-12 20:25:44 +01003946 char *cmd,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003947 BOOL inherit_handles,
Bram Moolenaar3f1138e2014-01-05 13:29:26 +01003948 DWORD flags,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003949 STARTUPINFO *si,
3950 PROCESS_INFORMATION *pi)
3951{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003952#ifdef FEAT_MBYTE
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003953 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3954 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003955 WCHAR *wcmd = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003956
3957 if (wcmd != NULL)
3958 {
3959 BOOL ret;
3960 ret = CreateProcessW(
3961 NULL, /* Executable name */
3962 wcmd, /* Command to execute */
3963 NULL, /* Process security attributes */
3964 NULL, /* Thread security attributes */
3965 inherit_handles, /* Inherit handles */
3966 flags, /* Creation flags */
3967 NULL, /* Environment */
3968 NULL, /* Current directory */
Bram Moolenaar36c85b22013-12-12 20:25:44 +01003969 (LPSTARTUPINFOW)si, /* Startup information */
Bram Moolenaar910cffb2013-12-11 17:58:35 +01003970 pi); /* Process information */
3971 vim_free(wcmd);
3972 return ret;
3973 }
3974 }
3975#endif
3976 return CreateProcess(
3977 NULL, /* Executable name */
3978 cmd, /* Command to execute */
3979 NULL, /* Process security attributes */
3980 NULL, /* Thread security attributes */
3981 inherit_handles, /* Inherit handles */
3982 flags, /* Creation flags */
3983 NULL, /* Environment */
3984 NULL, /* Current directory */
3985 si, /* Startup information */
3986 pi); /* Process information */
3987}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003988
3989
3990#if defined(FEAT_GUI_W32) || defined(PROTO)
3991
3992/*
3993 * Specialised version of system() for Win32 GUI mode.
3994 * This version proceeds as follows:
3995 * 1. Create a console window for use by the subprocess
3996 * 2. Run the subprocess (it gets the allocated console by default)
3997 * 3. Wait for the subprocess to terminate and get its exit code
3998 * 4. Prompt the user to press a key to close the console window
3999 */
4000 static int
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004001mch_system_classic(char *cmd, int options)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004002{
4003 STARTUPINFO si;
4004 PROCESS_INFORMATION pi;
4005 DWORD ret = 0;
4006 HWND hwnd = GetFocus();
4007
4008 si.cb = sizeof(si);
4009 si.lpReserved = NULL;
4010 si.lpDesktop = NULL;
4011 si.lpTitle = NULL;
4012 si.dwFlags = STARTF_USESHOWWINDOW;
4013 /*
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004014 * It's nicer to run a filter command in a minimized window.
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004015 * Don't activate the window to keep focus on Vim.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004016 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004017 if (options & SHELL_DOOUT)
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004018 si.wShowWindow = SW_SHOWMINNOACTIVE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004019 else
4020 si.wShowWindow = SW_SHOWNORMAL;
4021 si.cbReserved2 = 0;
4022 si.lpReserved2 = NULL;
4023
Bram Moolenaar071d4272004-06-13 20:20:40 +00004024 /* Now, run the command */
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004025 vim_create_process(cmd, FALSE,
4026 CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE, &si, &pi);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004027
4028 /* Wait for the command to terminate before continuing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029 {
4030#ifdef FEAT_GUI
4031 int delay = 1;
4032
4033 /* Keep updating the window while waiting for the shell to finish. */
4034 for (;;)
4035 {
4036 MSG msg;
4037
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02004038 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004039 {
4040 TranslateMessage(&msg);
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02004041 pDispatchMessage(&msg);
Bram Moolenaare4195c52012-08-02 12:31:44 +02004042 delay = 1;
4043 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044 }
4045 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4046 break;
4047
4048 /* We start waiting for a very short time and then increase it, so
4049 * that we respond quickly when the process is quick, and don't
4050 * consume too much overhead when it's slow. */
4051 if (delay < 50)
4052 delay += 10;
4053 }
4054#else
4055 WaitForSingleObject(pi.hProcess, INFINITE);
4056#endif
4057
4058 /* Get the command exit code */
4059 GetExitCodeProcess(pi.hProcess, &ret);
4060 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004061
4062 /* Close the handles to the subprocess, so that it goes away */
4063 CloseHandle(pi.hThread);
4064 CloseHandle(pi.hProcess);
4065
4066 /* Try to get input focus back. Doesn't always work though. */
4067 PostMessage(hwnd, WM_SETFOCUS, 0, 0);
4068
4069 return ret;
4070}
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004071
4072/*
4073 * Thread launched by the gui to send the current buffer data to the
4074 * process. This way avoid to hang up vim totally if the children
4075 * process take a long time to process the lines.
4076 */
Bram Moolenaar4c38d662016-08-03 20:54:57 +02004077 static unsigned int __stdcall
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004078sub_process_writer(LPVOID param)
4079{
4080 HANDLE g_hChildStd_IN_Wr = param;
4081 linenr_T lnum = curbuf->b_op_start.lnum;
4082 DWORD len = 0;
4083 DWORD l;
4084 char_u *lp = ml_get(lnum);
4085 char_u *s;
4086 int written = 0;
4087
4088 for (;;)
4089 {
4090 l = (DWORD)STRLEN(lp + written);
4091 if (l == 0)
4092 len = 0;
4093 else if (lp[written] == NL)
4094 {
4095 /* NL -> NUL translation */
4096 WriteFile(g_hChildStd_IN_Wr, "", 1, &len, NULL);
4097 }
4098 else
4099 {
4100 s = vim_strchr(lp + written, NL);
4101 WriteFile(g_hChildStd_IN_Wr, (char *)lp + written,
4102 s == NULL ? l : (DWORD)(s - (lp + written)),
4103 &len, NULL);
4104 }
4105 if (len == (int)l)
4106 {
4107 /* Finished a line, add a NL, unless this line should not have
4108 * one. */
4109 if (lnum != curbuf->b_op_end.lnum
Bram Moolenaar34d72d42015-07-17 14:18:08 +02004110 || (!curbuf->b_p_bin
4111 && curbuf->b_p_fixeol)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004112 || (lnum != curbuf->b_no_eol_lnum
4113 && (lnum != curbuf->b_ml.ml_line_count
4114 || curbuf->b_p_eol)))
4115 {
Bram Moolenaaraf62ff32013-03-19 14:48:29 +01004116 WriteFile(g_hChildStd_IN_Wr, "\n", 1, (LPDWORD)&ignored, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004117 }
4118
4119 ++lnum;
4120 if (lnum > curbuf->b_op_end.lnum)
4121 break;
4122
4123 lp = ml_get(lnum);
4124 written = 0;
4125 }
4126 else if (len > 0)
4127 written += len;
4128 }
4129
4130 /* finished all the lines, close pipe */
4131 CloseHandle(g_hChildStd_IN_Wr);
Bram Moolenaar86f2cd52016-08-02 21:55:17 +02004132 return 0;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004133}
4134
4135
4136# define BUFLEN 100 /* length for buffer, stolen from unix version */
4137
4138/*
4139 * This function read from the children's stdout and write the
4140 * data on screen or in the buffer accordingly.
4141 */
4142 static void
4143dump_pipe(int options,
4144 HANDLE g_hChildStd_OUT_Rd,
4145 garray_T *ga,
4146 char_u buffer[],
4147 DWORD *buffer_off)
4148{
4149 DWORD availableBytes = 0;
4150 DWORD i;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004151 int ret;
4152 DWORD len;
4153 DWORD toRead;
4154 int repeatCount;
4155
4156 /* we query the pipe to see if there is any data to read
4157 * to avoid to perform a blocking read */
4158 ret = PeekNamedPipe(g_hChildStd_OUT_Rd, /* pipe to query */
4159 NULL, /* optional buffer */
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02004160 0, /* buffer size */
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004161 NULL, /* number of read bytes */
4162 &availableBytes, /* available bytes total */
4163 NULL); /* byteLeft */
4164
4165 repeatCount = 0;
4166 /* We got real data in the pipe, read it */
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02004167 while (ret != 0 && availableBytes > 0)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004168 {
4169 repeatCount++;
4170 toRead =
4171# ifdef FEAT_MBYTE
4172 (DWORD)(BUFLEN - *buffer_off);
4173# else
4174 (DWORD)BUFLEN;
4175# endif
4176 toRead = availableBytes < toRead ? availableBytes : toRead;
4177 ReadFile(g_hChildStd_OUT_Rd, buffer
4178# ifdef FEAT_MBYTE
4179 + *buffer_off, toRead
4180# else
4181 , toRead
4182# endif
4183 , &len, NULL);
4184
4185 /* If we haven't read anything, there is a problem */
4186 if (len == 0)
4187 break;
4188
4189 availableBytes -= len;
4190
4191 if (options & SHELL_READ)
4192 {
4193 /* Do NUL -> NL translation, append NL separated
4194 * lines to the current buffer. */
4195 for (i = 0; i < len; ++i)
4196 {
4197 if (buffer[i] == NL)
4198 append_ga_line(ga);
4199 else if (buffer[i] == NUL)
4200 ga_append(ga, NL);
4201 else
4202 ga_append(ga, buffer[i]);
4203 }
4204 }
4205# ifdef FEAT_MBYTE
4206 else if (has_mbyte)
4207 {
4208 int l;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004209 int c;
4210 char_u *p;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004211
4212 len += *buffer_off;
4213 buffer[len] = NUL;
4214
4215 /* Check if the last character in buffer[] is
4216 * incomplete, keep these bytes for the next
4217 * round. */
4218 for (p = buffer; p < buffer + len; p += l)
4219 {
Bram Moolenaard3c907b2016-08-17 21:32:09 +02004220 l = MB_CPTR2LEN(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004221 if (l == 0)
4222 l = 1; /* NUL byte? */
4223 else if (MB_BYTE2LEN(*p) != l)
4224 break;
4225 }
4226 if (p == buffer) /* no complete character */
4227 {
4228 /* avoid getting stuck at an illegal byte */
4229 if (len >= 12)
4230 ++p;
4231 else
4232 {
4233 *buffer_off = len;
4234 return;
4235 }
4236 }
4237 c = *p;
4238 *p = NUL;
4239 msg_puts(buffer);
4240 if (p < buffer + len)
4241 {
4242 *p = c;
4243 *buffer_off = (DWORD)((buffer + len) - p);
4244 mch_memmove(buffer, p, *buffer_off);
4245 return;
4246 }
4247 *buffer_off = 0;
4248 }
4249# endif /* FEAT_MBYTE */
4250 else
4251 {
4252 buffer[len] = NUL;
4253 msg_puts(buffer);
4254 }
4255
4256 windgoto(msg_row, msg_col);
4257 cursor_on();
4258 out_flush();
4259 }
4260}
4261
4262/*
4263 * Version of system to use for windows NT > 5.0 (Win2K), use pipe
4264 * for communication and doesn't open any new window.
4265 */
4266 static int
4267mch_system_piped(char *cmd, int options)
4268{
4269 STARTUPINFO si;
4270 PROCESS_INFORMATION pi;
4271 DWORD ret = 0;
4272
4273 HANDLE g_hChildStd_IN_Rd = NULL;
4274 HANDLE g_hChildStd_IN_Wr = NULL;
4275 HANDLE g_hChildStd_OUT_Rd = NULL;
4276 HANDLE g_hChildStd_OUT_Wr = NULL;
4277
4278 char_u buffer[BUFLEN + 1]; /* reading buffer + size */
4279 DWORD len;
4280
4281 /* buffer used to receive keys */
4282 char_u ta_buf[BUFLEN + 1]; /* TypeAHead */
4283 int ta_len = 0; /* valid bytes in ta_buf[] */
4284
4285 DWORD i;
4286 int c;
4287 int noread_cnt = 0;
4288 garray_T ga;
4289 int delay = 1;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004290 DWORD buffer_off = 0; /* valid bytes in buffer[] */
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004291 char *p = NULL;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004292
4293 SECURITY_ATTRIBUTES saAttr;
4294
4295 /* Set the bInheritHandle flag so pipe handles are inherited. */
4296 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
4297 saAttr.bInheritHandle = TRUE;
4298 saAttr.lpSecurityDescriptor = NULL;
4299
4300 if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)
4301 /* Ensure the read handle to the pipe for STDOUT is not inherited. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004302 || ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004303 /* Create a pipe for the child process's STDIN. */
4304 || ! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)
4305 /* Ensure the write handle to the pipe for STDIN is not inherited. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004306 || ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004307 {
4308 CloseHandle(g_hChildStd_IN_Rd);
4309 CloseHandle(g_hChildStd_IN_Wr);
4310 CloseHandle(g_hChildStd_OUT_Rd);
4311 CloseHandle(g_hChildStd_OUT_Wr);
4312 MSG_PUTS(_("\nCannot create pipes\n"));
4313 }
4314
4315 si.cb = sizeof(si);
4316 si.lpReserved = NULL;
4317 si.lpDesktop = NULL;
4318 si.lpTitle = NULL;
4319 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
4320
4321 /* set-up our file redirection */
4322 si.hStdError = g_hChildStd_OUT_Wr;
4323 si.hStdOutput = g_hChildStd_OUT_Wr;
4324 si.hStdInput = g_hChildStd_IN_Rd;
4325 si.wShowWindow = SW_HIDE;
4326 si.cbReserved2 = 0;
4327 si.lpReserved2 = NULL;
4328
4329 if (options & SHELL_READ)
4330 ga_init2(&ga, 1, BUFLEN);
4331
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004332 if (cmd != NULL)
4333 {
4334 p = (char *)vim_strsave((char_u *)cmd);
4335 if (p != NULL)
4336 unescape_shellxquote((char_u *)p, p_sxe);
4337 else
4338 p = cmd;
4339 }
4340
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004341 /* Now, run the command.
4342 * About "Inherit handles" being TRUE: this command can be litigious,
4343 * handle inheritance was deactivated for pending temp file, but, if we
4344 * deactivate it, the pipes don't work for some reason. */
4345 vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE, &si, &pi);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004346
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004347 if (p != cmd)
4348 vim_free(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004349
4350 /* Close our unused side of the pipes */
4351 CloseHandle(g_hChildStd_IN_Rd);
4352 CloseHandle(g_hChildStd_OUT_Wr);
4353
4354 if (options & SHELL_WRITE)
4355 {
Bram Moolenaar86f2cd52016-08-02 21:55:17 +02004356 HANDLE thread = (HANDLE)
4357 _beginthreadex(NULL, /* security attributes */
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004358 0, /* default stack size */
4359 sub_process_writer, /* function to be executed */
4360 g_hChildStd_IN_Wr, /* parameter */
4361 0, /* creation flag, start immediately */
4362 NULL); /* we don't care about thread id */
4363 CloseHandle(thread);
4364 g_hChildStd_IN_Wr = NULL;
4365 }
4366
4367 /* Keep updating the window while waiting for the shell to finish. */
4368 for (;;)
4369 {
4370 MSG msg;
4371
Bram Moolenaar175d0702013-12-11 18:36:33 +01004372 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004373 {
4374 TranslateMessage(&msg);
Bram Moolenaar175d0702013-12-11 18:36:33 +01004375 pDispatchMessage(&msg);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004376 }
4377
4378 /* write pipe information in the window */
4379 if ((options & (SHELL_READ|SHELL_WRITE))
4380# ifdef FEAT_GUI
4381 || gui.in_use
4382# endif
4383 )
4384 {
4385 len = 0;
4386 if (!(options & SHELL_EXPAND)
4387 && ((options &
4388 (SHELL_READ|SHELL_WRITE|SHELL_COOKED))
4389 != (SHELL_READ|SHELL_WRITE|SHELL_COOKED)
4390# ifdef FEAT_GUI
4391 || gui.in_use
4392# endif
4393 )
4394 && (ta_len > 0 || noread_cnt > 4))
4395 {
4396 if (ta_len == 0)
4397 {
4398 /* Get extra characters when we don't have any. Reset the
4399 * counter and timer. */
4400 noread_cnt = 0;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004401 len = ui_inchar(ta_buf, BUFLEN, 10L, 0);
4402 }
4403 if (ta_len > 0 || len > 0)
4404 {
4405 /*
4406 * For pipes: Check for CTRL-C: send interrupt signal to
4407 * child. Check for CTRL-D: EOF, close pipe to child.
4408 */
4409 if (len == 1 && cmd != NULL)
4410 {
4411 if (ta_buf[ta_len] == Ctrl_C)
4412 {
4413 /* Learn what exit code is expected, for
4414 * now put 9 as SIGKILL */
4415 TerminateProcess(pi.hProcess, 9);
4416 }
4417 if (ta_buf[ta_len] == Ctrl_D)
4418 {
4419 CloseHandle(g_hChildStd_IN_Wr);
4420 g_hChildStd_IN_Wr = NULL;
4421 }
4422 }
4423
4424 /* replace K_BS by <BS> and K_DEL by <DEL> */
4425 for (i = ta_len; i < ta_len + len; ++i)
4426 {
4427 if (ta_buf[i] == CSI && len - i > 2)
4428 {
4429 c = TERMCAP2KEY(ta_buf[i + 1], ta_buf[i + 2]);
4430 if (c == K_DEL || c == K_KDEL || c == K_BS)
4431 {
4432 mch_memmove(ta_buf + i + 1, ta_buf + i + 3,
4433 (size_t)(len - i - 2));
4434 if (c == K_DEL || c == K_KDEL)
4435 ta_buf[i] = DEL;
4436 else
4437 ta_buf[i] = Ctrl_H;
4438 len -= 2;
4439 }
4440 }
4441 else if (ta_buf[i] == '\r')
4442 ta_buf[i] = '\n';
4443# ifdef FEAT_MBYTE
4444 if (has_mbyte)
4445 i += (*mb_ptr2len_len)(ta_buf + i,
4446 ta_len + len - i) - 1;
4447# endif
4448 }
4449
4450 /*
4451 * For pipes: echo the typed characters. For a pty this
4452 * does not seem to work.
4453 */
4454 for (i = ta_len; i < ta_len + len; ++i)
4455 {
4456 if (ta_buf[i] == '\n' || ta_buf[i] == '\b')
4457 msg_putchar(ta_buf[i]);
4458# ifdef FEAT_MBYTE
4459 else if (has_mbyte)
4460 {
4461 int l = (*mb_ptr2len)(ta_buf + i);
4462
4463 msg_outtrans_len(ta_buf + i, l);
4464 i += l - 1;
4465 }
4466# endif
4467 else
4468 msg_outtrans_len(ta_buf + i, 1);
4469 }
4470 windgoto(msg_row, msg_col);
4471 out_flush();
4472
4473 ta_len += len;
4474
4475 /*
4476 * Write the characters to the child, unless EOF has been
4477 * typed for pipes. Write one character at a time, to
4478 * avoid losing too much typeahead. When writing buffer
4479 * lines, drop the typed characters (only check for
4480 * CTRL-C).
4481 */
4482 if (options & SHELL_WRITE)
4483 ta_len = 0;
4484 else if (g_hChildStd_IN_Wr != NULL)
4485 {
4486 WriteFile(g_hChildStd_IN_Wr, (char*)ta_buf,
4487 1, &len, NULL);
4488 // if we are typing in, we want to keep things reactive
4489 delay = 1;
4490 if (len > 0)
4491 {
4492 ta_len -= len;
4493 mch_memmove(ta_buf, ta_buf + len, ta_len);
4494 }
4495 }
4496 }
4497 }
4498 }
4499
4500 if (ta_len)
4501 ui_inchar_undo(ta_buf, ta_len);
4502
4503 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4504 {
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004505 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004506 break;
4507 }
4508
4509 ++noread_cnt;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004510 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004511
4512 /* We start waiting for a very short time and then increase it, so
4513 * that we respond quickly when the process is quick, and don't
4514 * consume too much overhead when it's slow. */
4515 if (delay < 50)
4516 delay += 10;
4517 }
4518
4519 /* Close the pipe */
4520 CloseHandle(g_hChildStd_OUT_Rd);
4521 if (g_hChildStd_IN_Wr != NULL)
4522 CloseHandle(g_hChildStd_IN_Wr);
4523
4524 WaitForSingleObject(pi.hProcess, INFINITE);
4525
4526 /* Get the command exit code */
4527 GetExitCodeProcess(pi.hProcess, &ret);
4528
4529 if (options & SHELL_READ)
4530 {
4531 if (ga.ga_len > 0)
4532 {
4533 append_ga_line(&ga);
4534 /* remember that the NL was missing */
4535 curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
4536 }
4537 else
4538 curbuf->b_no_eol_lnum = 0;
4539 ga_clear(&ga);
4540 }
4541
4542 /* Close the handles to the subprocess, so that it goes away */
4543 CloseHandle(pi.hThread);
4544 CloseHandle(pi.hProcess);
4545
4546 return ret;
4547}
4548
4549 static int
4550mch_system(char *cmd, int options)
4551{
4552 /* if we can pipe and the shelltemp option is off */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004553 if (!p_stmp)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004554 return mch_system_piped(cmd, options);
4555 else
4556 return mch_system_classic(cmd, options);
4557}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004558#else
4559
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004560# ifdef FEAT_MBYTE
4561 static int
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01004562mch_system(char *cmd, int options)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004563{
4564 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4565 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004566 WCHAR *wcmd = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004567 if (wcmd != NULL)
4568 {
4569 int ret = _wsystem(wcmd);
4570 vim_free(wcmd);
4571 return ret;
4572 }
4573 }
4574 return system(cmd);
4575}
4576# else
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01004577# define mch_system(c, o) system(c)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004578# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579
4580#endif
4581
4582/*
4583 * Either execute a command by calling the shell or start a new shell
4584 */
4585 int
4586mch_call_shell(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004587 char_u *cmd,
4588 int options) /* SHELL_*, see vim.h */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004589{
4590 int x = 0;
4591 int tmode = cur_tmode;
4592#ifdef FEAT_TITLE
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004593 char szShellTitle[512];
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004594# ifdef FEAT_MBYTE
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004595 int did_set_title = FALSE;
4596
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004597 /* Change the title to reflect that we are in a subshell. */
4598 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4599 {
4600 WCHAR szShellTitle[512];
4601
4602 if (GetConsoleTitleW(szShellTitle,
4603 sizeof(szShellTitle)/sizeof(WCHAR) - 4) > 0)
4604 {
4605 if (cmd == NULL)
4606 wcscat(szShellTitle, L" :sh");
4607 else
4608 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004609 WCHAR *wn = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004610
4611 if (wn != NULL)
4612 {
4613 wcscat(szShellTitle, L" - !");
4614 if ((wcslen(szShellTitle) + wcslen(wn) <
4615 sizeof(szShellTitle)/sizeof(WCHAR)))
4616 wcscat(szShellTitle, wn);
4617 SetConsoleTitleW(szShellTitle);
4618 vim_free(wn);
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004619 did_set_title = TRUE;
Bram Moolenaar1df52d72014-10-15 22:50:10 +02004620 }
4621 }
4622 }
4623 }
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004624 if (!did_set_title)
4625# endif
4626 /* Change the title to reflect that we are in a subshell. */
4627 if (GetConsoleTitle(szShellTitle, sizeof(szShellTitle) - 4) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004628 {
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004629 if (cmd == NULL)
4630 strcat(szShellTitle, " :sh");
4631 else
4632 {
4633 strcat(szShellTitle, " - !");
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004634 if ((strlen(szShellTitle) + strlen((char *)cmd)
4635 < sizeof(szShellTitle)))
4636 strcat(szShellTitle, (char *)cmd);
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02004637 }
4638 SetConsoleTitle(szShellTitle);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004639 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004640#endif
4641
4642 out_flush();
4643
4644#ifdef MCH_WRITE_DUMP
4645 if (fdDump)
4646 {
4647 fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
4648 fflush(fdDump);
4649 }
4650#endif
4651
4652 /*
4653 * Catch all deadly signals while running the external command, because a
4654 * CTRL-C, Ctrl-Break or illegal instruction might otherwise kill us.
4655 */
4656 signal(SIGINT, SIG_IGN);
4657#if defined(__GNUC__) && !defined(__MINGW32__)
4658 signal(SIGKILL, SIG_IGN);
4659#else
4660 signal(SIGBREAK, SIG_IGN);
4661#endif
4662 signal(SIGILL, SIG_IGN);
4663 signal(SIGFPE, SIG_IGN);
4664 signal(SIGSEGV, SIG_IGN);
4665 signal(SIGTERM, SIG_IGN);
4666 signal(SIGABRT, SIG_IGN);
4667
4668 if (options & SHELL_COOKED)
4669 settmode(TMODE_COOK); /* set to normal mode */
4670
4671 if (cmd == NULL)
4672 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004673 x = mch_system((char *)p_sh, options);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674 }
4675 else
4676 {
4677 /* we use "command" or "cmd" to start the shell; slow but easy */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004678 char_u *newcmd = NULL;
4679 char_u *cmdbase = cmd;
4680 long_u cmdlen;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004681
4682 /* Skip a leading ", ( and "(. */
4683 if (*cmdbase == '"' )
4684 ++cmdbase;
4685 if (*cmdbase == '(')
4686 ++cmdbase;
4687
4688 if ((STRNICMP(cmdbase, "start", 5) == 0) && vim_iswhite(cmdbase[5]))
4689 {
4690 STARTUPINFO si;
4691 PROCESS_INFORMATION pi;
4692 DWORD flags = CREATE_NEW_CONSOLE;
4693 char_u *p;
4694
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01004695 ZeroMemory(&si, sizeof(si));
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004696 si.cb = sizeof(si);
4697 si.lpReserved = NULL;
4698 si.lpDesktop = NULL;
4699 si.lpTitle = NULL;
4700 si.dwFlags = 0;
4701 si.cbReserved2 = 0;
4702 si.lpReserved2 = NULL;
4703
4704 cmdbase = skipwhite(cmdbase + 5);
4705 if ((STRNICMP(cmdbase, "/min", 4) == 0)
4706 && vim_iswhite(cmdbase[4]))
4707 {
4708 cmdbase = skipwhite(cmdbase + 4);
4709 si.dwFlags = STARTF_USESHOWWINDOW;
4710 si.wShowWindow = SW_SHOWMINNOACTIVE;
4711 }
4712 else if ((STRNICMP(cmdbase, "/b", 2) == 0)
4713 && vim_iswhite(cmdbase[2]))
4714 {
4715 cmdbase = skipwhite(cmdbase + 2);
4716 flags = CREATE_NO_WINDOW;
4717 si.dwFlags = STARTF_USESTDHANDLES;
4718 si.hStdInput = CreateFile("\\\\.\\NUL", // File name
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004719 GENERIC_READ, // Access flags
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004720 0, // Share flags
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004721 NULL, // Security att.
4722 OPEN_EXISTING, // Open flags
4723 FILE_ATTRIBUTE_NORMAL, // File att.
4724 NULL); // Temp file
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004725 si.hStdOutput = si.hStdInput;
4726 si.hStdError = si.hStdInput;
4727 }
4728
4729 /* Remove a trailing ", ) and )" if they have a match
4730 * at the start of the command. */
4731 if (cmdbase > cmd)
4732 {
4733 p = cmdbase + STRLEN(cmdbase);
4734 if (p > cmdbase && p[-1] == '"' && *cmd == '"')
4735 *--p = NUL;
4736 if (p > cmdbase && p[-1] == ')'
4737 && (*cmd =='(' || cmd[1] == '('))
4738 *--p = NUL;
4739 }
4740
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004741 newcmd = cmdbase;
4742 unescape_shellxquote(cmdbase, p_sxe);
4743
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004744 /*
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004745 * If creating new console, arguments are passed to the
4746 * 'cmd.exe' as-is. If it's not, arguments are not treated
4747 * correctly for current 'cmd.exe'. So unescape characters in
4748 * shellxescape except '|' for avoiding to be treated as
4749 * argument to them. Pass the arguments to sub-shell.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004750 */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004751 if (flags != CREATE_NEW_CONSOLE)
4752 {
4753 char_u *subcmd;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004754 char_u *cmd_shell = mch_getenv("COMSPEC");
4755
4756 if (cmd_shell == NULL || *cmd_shell == NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004757 cmd_shell = (char_u *)default_shell();
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004758
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004759 subcmd = vim_strsave_escaped_ext(cmdbase,
4760 (char_u *)"|", '^', FALSE);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004761 if (subcmd != NULL)
4762 {
4763 /* make "cmd.exe /c arguments" */
4764 cmdlen = STRLEN(cmd_shell) + STRLEN(subcmd) + 5;
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004765 newcmd = lalloc(cmdlen, TRUE);
4766 if (newcmd != NULL)
4767 vim_snprintf((char *)newcmd, cmdlen, "%s /c %s",
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004768 cmd_shell, subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004769 else
4770 newcmd = cmdbase;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004771 vim_free(subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004772 }
4773 }
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004774
4775 /*
4776 * Now, start the command as a process, so that it doesn't
4777 * inherit our handles which causes unpleasant dangling swap
4778 * files if we exit before the spawned process
4779 */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004780 if (vim_create_process((char *)newcmd, FALSE, flags, &si, &pi))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004781 x = 0;
4782 else
4783 {
4784 x = -1;
4785#ifdef FEAT_GUI_W32
4786 EMSG(_("E371: Command not found"));
4787#endif
4788 }
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004789
4790 if (newcmd != cmdbase)
4791 vim_free(newcmd);
4792
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01004793 if (si.dwFlags == STARTF_USESTDHANDLES && si.hStdInput != NULL)
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004794 {
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01004795 /* Close the handle to \\.\NUL created above. */
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004796 CloseHandle(si.hStdInput);
4797 }
4798 /* Close the handles to the subprocess, so that it goes away */
4799 CloseHandle(pi.hThread);
4800 CloseHandle(pi.hProcess);
4801 }
4802 else
4803 {
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004804 cmdlen = (
Bram Moolenaar071d4272004-06-13 20:20:40 +00004805#ifdef FEAT_GUI_W32
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004806 (!p_stmp ? 0 : STRLEN(vimrun_path)) +
Bram Moolenaar071d4272004-06-13 20:20:40 +00004807#endif
Bram Moolenaar0fde2902008-03-16 13:54:13 +00004808 STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10);
4809
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004810 newcmd = lalloc(cmdlen, TRUE);
4811 if (newcmd != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004812 {
4813#if defined(FEAT_GUI_W32)
4814 if (need_vimrun_warning)
4815 {
Bram Moolenaar63e43442016-11-19 17:28:44 +01004816 char *msg = _("VIMRUN.EXE not found in your $PATH.\n"
4817 "External commands will not pause after completion.\n"
4818 "See :help win32-vimrun for more information.");
4819 char *title = _("Vim Warning");
4820# ifdef FEAT_MBYTE
4821 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4822 {
4823 WCHAR *wmsg = enc_to_utf16((char_u *)msg, NULL);
4824 WCHAR *wtitle = enc_to_utf16((char_u *)title, NULL);
4825
4826 if (wmsg != NULL && wtitle != NULL)
4827 MessageBoxW(NULL, wmsg, wtitle, MB_ICONWARNING);
4828 vim_free(wmsg);
4829 vim_free(wtitle);
4830 }
4831 else
4832# endif
4833 MessageBox(NULL, msg, title, MB_ICONWARNING);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004834 need_vimrun_warning = FALSE;
4835 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004836 if (!s_dont_use_vimrun && p_stmp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004837 /* Use vimrun to execute the command. It opens a console
4838 * window, which can be closed without killing Vim. */
Bram Moolenaarcc448b32010-07-14 16:52:17 +02004839 vim_snprintf((char *)newcmd, cmdlen, "%s%s%s %s %s",
Bram Moolenaar071d4272004-06-13 20:20:40 +00004840 vimrun_path,
4841 (msg_silent != 0 || (options & SHELL_DOOUT))
4842 ? "-s " : "",
4843 p_sh, p_shcf, cmd);
4844 else
4845#endif
Bram Moolenaarcc448b32010-07-14 16:52:17 +02004846 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s",
Bram Moolenaar0fde2902008-03-16 13:54:13 +00004847 p_sh, p_shcf, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004848 x = mch_system((char *)newcmd, options);
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004849 vim_free(newcmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004850 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004851 }
4852 }
4853
4854 if (tmode == TMODE_RAW)
4855 settmode(TMODE_RAW); /* set to raw mode */
4856
4857 /* Print the return value, unless "vimrun" was used. */
4858 if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent
4859#if defined(FEAT_GUI_W32)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004860 && ((options & SHELL_DOOUT) || s_dont_use_vimrun || !p_stmp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004861#endif
4862 )
4863 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004864 smsg((char_u *)_("shell returned %d"), x);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004865 msg_putchar('\n');
4866 }
4867#ifdef FEAT_TITLE
4868 resettitle();
4869#endif
4870
4871 signal(SIGINT, SIG_DFL);
4872#if defined(__GNUC__) && !defined(__MINGW32__)
4873 signal(SIGKILL, SIG_DFL);
4874#else
4875 signal(SIGBREAK, SIG_DFL);
4876#endif
4877 signal(SIGILL, SIG_DFL);
4878 signal(SIGFPE, SIG_DFL);
4879 signal(SIGSEGV, SIG_DFL);
4880 signal(SIGTERM, SIG_DFL);
4881 signal(SIGABRT, SIG_DFL);
4882
4883 return x;
4884}
4885
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01004886#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01004887 static HANDLE
4888job_io_file_open(
Bram Moolenaar972c3b82017-01-12 21:44:49 +01004889 char_u *fname,
4890 DWORD dwDesiredAccess,
4891 DWORD dwShareMode,
4892 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
4893 DWORD dwCreationDisposition,
4894 DWORD dwFlagsAndAttributes)
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01004895{
4896 HANDLE h;
4897# ifdef FEAT_MBYTE
4898 WCHAR *wn = NULL;
4899 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4900 {
Bram Moolenaar972c3b82017-01-12 21:44:49 +01004901 wn = enc_to_utf16(fname, NULL);
4902 if (wn != NULL)
4903 {
4904 h = CreateFileW(wn, dwDesiredAccess, dwShareMode,
4905 lpSecurityAttributes, dwCreationDisposition,
4906 dwFlagsAndAttributes, NULL);
4907 vim_free(wn);
4908 }
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01004909 }
4910 if (wn == NULL)
4911# endif
Bram Moolenaar972c3b82017-01-12 21:44:49 +01004912 h = CreateFile((LPCSTR)fname, dwDesiredAccess, dwShareMode,
4913 lpSecurityAttributes, dwCreationDisposition,
4914 dwFlagsAndAttributes, NULL);
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01004915 return h;
4916}
4917
Bram Moolenaar942d6b22016-02-07 19:57:16 +01004918 void
Bram Moolenaar9a6e33a2016-02-16 19:25:12 +01004919mch_start_job(char *cmd, job_T *job, jobopt_T *options)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01004920{
4921 STARTUPINFO si;
4922 PROCESS_INFORMATION pi;
Bram Moolenaar14207f42016-10-27 21:13:10 +02004923 HANDLE jo;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01004924 SECURITY_ATTRIBUTES saAttr;
4925 channel_T *channel = NULL;
Bram Moolenaard8070362016-02-15 21:56:54 +01004926 HANDLE ifd[2];
4927 HANDLE ofd[2];
4928 HANDLE efd[2];
Bram Moolenaard5d3d302016-03-09 20:54:51 +01004929
4930 int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL;
4931 int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL;
4932 int use_null_for_err = options->jo_io[PART_ERR] == JIO_NULL;
4933 int use_file_for_in = options->jo_io[PART_IN] == JIO_FILE;
4934 int use_file_for_out = options->jo_io[PART_OUT] == JIO_FILE;
4935 int use_file_for_err = options->jo_io[PART_ERR] == JIO_FILE;
4936 int use_out_for_err = options->jo_io[PART_ERR] == JIO_OUT;
4937
4938 if (use_out_for_err && use_null_for_out)
4939 use_null_for_err = TRUE;
Bram Moolenaard8070362016-02-15 21:56:54 +01004940
4941 ifd[0] = INVALID_HANDLE_VALUE;
4942 ifd[1] = INVALID_HANDLE_VALUE;
4943 ofd[0] = INVALID_HANDLE_VALUE;
4944 ofd[1] = INVALID_HANDLE_VALUE;
4945 efd[0] = INVALID_HANDLE_VALUE;
4946 efd[1] = INVALID_HANDLE_VALUE;
Bram Moolenaar942d6b22016-02-07 19:57:16 +01004947
Bram Moolenaar14207f42016-10-27 21:13:10 +02004948 jo = CreateJobObject(NULL, NULL);
4949 if (jo == NULL)
4950 {
4951 job->jv_status = JOB_FAILED;
4952 goto failed;
4953 }
4954
Bram Moolenaar76467df2016-02-12 19:30:26 +01004955 ZeroMemory(&pi, sizeof(pi));
Bram Moolenaar942d6b22016-02-07 19:57:16 +01004956 ZeroMemory(&si, sizeof(si));
4957 si.cb = sizeof(si);
Bram Moolenaard8070362016-02-15 21:56:54 +01004958 si.dwFlags |= STARTF_USESHOWWINDOW;
Bram Moolenaar76467df2016-02-12 19:30:26 +01004959 si.wShowWindow = SW_HIDE;
Bram Moolenaar942d6b22016-02-07 19:57:16 +01004960
Bram Moolenaard8070362016-02-15 21:56:54 +01004961 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
4962 saAttr.bInheritHandle = TRUE;
4963 saAttr.lpSecurityDescriptor = NULL;
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01004964
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01004965 if (use_file_for_in)
4966 {
4967 char_u *fname = options->jo_io_name[PART_IN];
4968
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01004969 ifd[0] = job_io_file_open(fname, GENERIC_READ,
4970 FILE_SHARE_READ | FILE_SHARE_WRITE,
4971 &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL);
4972 if (ifd[0] == INVALID_HANDLE_VALUE)
Bram Moolenaar94d01912016-03-08 13:48:51 +01004973 {
4974 EMSG2(_(e_notopen), fname);
4975 goto failed;
4976 }
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01004977 }
Bram Moolenaard5d3d302016-03-09 20:54:51 +01004978 else if (!use_null_for_in &&
4979 (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004980 || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01004981 goto failed;
4982
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01004983 if (use_file_for_out)
4984 {
4985 char_u *fname = options->jo_io_name[PART_OUT];
4986
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01004987 ofd[1] = job_io_file_open(fname, GENERIC_WRITE,
4988 FILE_SHARE_READ | FILE_SHARE_WRITE,
4989 &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL);
4990 if (ofd[1] == INVALID_HANDLE_VALUE)
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01004991 {
4992 EMSG2(_(e_notopen), fname);
4993 goto failed;
4994 }
4995 }
Bram Moolenaard5d3d302016-03-09 20:54:51 +01004996 else if (!use_null_for_out &&
4997 (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004998 || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01004999 goto failed;
5000
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005001 if (use_file_for_err)
5002 {
5003 char_u *fname = options->jo_io_name[PART_ERR];
5004
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005005 efd[1] = job_io_file_open(fname, GENERIC_WRITE,
5006 FILE_SHARE_READ | FILE_SHARE_WRITE,
5007 &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL);
5008 if (efd[1] == INVALID_HANDLE_VALUE)
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005009 {
5010 EMSG2(_(e_notopen), fname);
5011 goto failed;
5012 }
5013 }
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005014 else if (!use_out_for_err && !use_null_for_err &&
5015 (!CreatePipe(&efd[0], &efd[1], &saAttr, 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005016 || !SetHandleInformation(efd[0], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaard8070362016-02-15 21:56:54 +01005017 goto failed;
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005018
Bram Moolenaard8070362016-02-15 21:56:54 +01005019 si.dwFlags |= STARTF_USESTDHANDLES;
5020 si.hStdInput = ifd[0];
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005021 si.hStdOutput = ofd[1];
5022 si.hStdError = use_out_for_err ? ofd[1] : efd[1];
5023
5024 if (!use_null_for_in || !use_null_for_out || !use_null_for_err)
5025 {
Bram Moolenaarde279892016-03-11 22:19:44 +01005026 if (options->jo_set & JO_CHANNEL)
5027 {
5028 channel = options->jo_channel;
5029 if (channel != NULL)
5030 ++channel->ch_refcount;
5031 }
5032 else
5033 channel = add_channel();
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005034 if (channel == NULL)
5035 goto failed;
5036 }
Bram Moolenaard8070362016-02-15 21:56:54 +01005037
5038 if (!vim_create_process(cmd, TRUE,
Bram Moolenaar14207f42016-10-27 21:13:10 +02005039 CREATE_SUSPENDED |
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005040 CREATE_DEFAULT_ERROR_MODE |
5041 CREATE_NEW_PROCESS_GROUP |
Bram Moolenaar76467df2016-02-12 19:30:26 +01005042 CREATE_NEW_CONSOLE,
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005043 &si, &pi))
Bram Moolenaar76467df2016-02-12 19:30:26 +01005044 {
Bram Moolenaar14207f42016-10-27 21:13:10 +02005045 CloseHandle(jo);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005046 job->jv_status = JOB_FAILED;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005047 goto failed;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005048 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005049
Bram Moolenaar14207f42016-10-27 21:13:10 +02005050 if (!AssignProcessToJobObject(jo, pi.hProcess))
5051 {
5052 /* if failing, switch the way to terminate
5053 * process with TerminateProcess. */
5054 CloseHandle(jo);
5055 jo = NULL;
5056 }
5057 ResumeThread(pi.hThread);
Bram Moolenaar75578a32016-03-10 16:33:31 +01005058 CloseHandle(pi.hThread);
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005059 job->jv_proc_info = pi;
Bram Moolenaar14207f42016-10-27 21:13:10 +02005060 job->jv_job_object = jo;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005061 job->jv_status = JOB_STARTED;
5062
Bram Moolenaar641ad6c2016-09-01 18:32:11 +02005063 CloseHandle(ifd[0]);
5064 CloseHandle(ofd[1]);
5065 if (!use_out_for_err && !use_null_for_err)
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005066 CloseHandle(efd[1]);
Bram Moolenaard8070362016-02-15 21:56:54 +01005067
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005068 job->jv_channel = channel;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005069 if (channel != NULL)
5070 {
5071 channel_set_pipes(channel,
5072 use_file_for_in || use_null_for_in
5073 ? INVALID_FD : (sock_T)ifd[1],
5074 use_file_for_out || use_null_for_out
5075 ? INVALID_FD : (sock_T)ofd[0],
5076 use_out_for_err || use_file_for_err || use_null_for_err
5077 ? INVALID_FD : (sock_T)efd[0]);
5078 channel_set_job(channel, job, options);
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005079 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005080 return;
5081
5082failed:
Bram Moolenaard8070362016-02-15 21:56:54 +01005083 CloseHandle(ifd[0]);
5084 CloseHandle(ofd[0]);
5085 CloseHandle(efd[0]);
5086 CloseHandle(ifd[1]);
5087 CloseHandle(ofd[1]);
5088 CloseHandle(efd[1]);
Bram Moolenaarde279892016-03-11 22:19:44 +01005089 channel_unref(channel);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005090}
5091
5092 char *
5093mch_job_status(job_T *job)
5094{
5095 DWORD dwExitCode = 0;
5096
Bram Moolenaar76467df2016-02-12 19:30:26 +01005097 if (!GetExitCodeProcess(job->jv_proc_info.hProcess, &dwExitCode)
5098 || dwExitCode != STILL_ACTIVE)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005099 {
Bram Moolenaareab089d2016-02-21 19:32:02 +01005100 job->jv_exitval = (int)dwExitCode;
Bram Moolenaar7df915d2016-11-17 17:25:32 +01005101 if (job->jv_status < JOB_ENDED)
Bram Moolenaar97792de2016-10-15 18:36:49 +02005102 {
5103 ch_log(job->jv_channel, "Job ended");
5104 job->jv_status = JOB_ENDED;
5105 }
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005106 return "dead";
5107 }
5108 return "run";
5109}
5110
Bram Moolenaar97792de2016-10-15 18:36:49 +02005111 job_T *
5112mch_detect_ended_job(job_T *job_list)
5113{
5114 HANDLE jobHandles[MAXIMUM_WAIT_OBJECTS];
5115 job_T *jobArray[MAXIMUM_WAIT_OBJECTS];
5116 job_T *job = job_list;
5117
5118 while (job != NULL)
5119 {
5120 DWORD n;
5121 DWORD result;
5122
5123 for (n = 0; n < MAXIMUM_WAIT_OBJECTS
5124 && job != NULL; job = job->jv_next)
5125 {
5126 if (job->jv_status == JOB_STARTED)
5127 {
5128 jobHandles[n] = job->jv_proc_info.hProcess;
5129 jobArray[n] = job;
5130 ++n;
5131 }
5132 }
5133 if (n == 0)
5134 continue;
5135 result = WaitForMultipleObjects(n, jobHandles, FALSE, 0);
5136 if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + n)
5137 {
5138 job_T *wait_job = jobArray[result - WAIT_OBJECT_0];
5139
5140 if (STRCMP(mch_job_status(wait_job), "dead") == 0)
5141 return wait_job;
5142 }
5143 }
5144 return NULL;
5145}
5146
Bram Moolenaarfb630902016-10-29 14:55:00 +02005147 static BOOL
5148terminate_all(HANDLE process, int code)
5149{
5150 PROCESSENTRY32 pe;
5151 HANDLE h = INVALID_HANDLE_VALUE;
5152 DWORD pid = GetProcessId(process);
5153
5154 if (pid != 0)
5155 {
5156 h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
5157 if (h != INVALID_HANDLE_VALUE)
5158 {
5159 pe.dwSize = sizeof(PROCESSENTRY32);
5160 if (!Process32First(h, &pe))
5161 goto theend;
5162
5163 do
5164 {
5165 if (pe.th32ParentProcessID == pid)
5166 {
5167 HANDLE ph = OpenProcess(
5168 PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
5169 if (ph != NULL)
5170 {
5171 terminate_all(ph, code);
5172 CloseHandle(ph);
5173 }
5174 }
5175 } while (Process32Next(h, &pe));
5176
5177 CloseHandle(h);
5178 }
5179 }
5180
5181theend:
5182 return TerminateProcess(process, code);
5183}
5184
5185/*
5186 * Send a (deadly) signal to "job".
5187 * Return FAIL if it didn't work.
5188 */
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005189 int
5190mch_stop_job(job_T *job, char_u *how)
5191{
Bram Moolenaar923d9262016-02-25 20:56:01 +01005192 int ret;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005193
Bram Moolenaar923d9262016-02-25 20:56:01 +01005194 if (STRCMP(how, "term") == 0 || STRCMP(how, "kill") == 0 || *how == NUL)
Bram Moolenaar76467df2016-02-12 19:30:26 +01005195 {
Bram Moolenaarfb630902016-10-29 14:55:00 +02005196 /* deadly signal */
Bram Moolenaar14207f42016-10-27 21:13:10 +02005197 if (job->jv_job_object != NULL)
5198 return TerminateJobObject(job->jv_job_object, 0) ? OK : FAIL;
Bram Moolenaarfb630902016-10-29 14:55:00 +02005199 return terminate_all(job->jv_proc_info.hProcess, 0) ? OK : FAIL;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005200 }
5201
5202 if (!AttachConsole(job->jv_proc_info.dwProcessId))
5203 return FAIL;
5204 ret = GenerateConsoleCtrlEvent(
Bram Moolenaar923d9262016-02-25 20:56:01 +01005205 STRCMP(how, "int") == 0 ? CTRL_C_EVENT : CTRL_BREAK_EVENT,
5206 job->jv_proc_info.dwProcessId)
5207 ? OK : FAIL;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005208 FreeConsole();
5209 return ret;
5210}
5211
5212/*
5213 * Clear the data related to "job".
5214 */
5215 void
5216mch_clear_job(job_T *job)
5217{
5218 if (job->jv_status != JOB_FAILED)
5219 {
Bram Moolenaar14207f42016-10-27 21:13:10 +02005220 if (job->jv_job_object != NULL)
5221 CloseHandle(job->jv_job_object);
Bram Moolenaar76467df2016-02-12 19:30:26 +01005222 CloseHandle(job->jv_proc_info.hProcess);
5223 }
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005224}
5225#endif
5226
Bram Moolenaar071d4272004-06-13 20:20:40 +00005227
5228#ifndef FEAT_GUI_W32
5229
5230/*
5231 * Start termcap mode
5232 */
5233 static void
5234termcap_mode_start(void)
5235{
5236 DWORD cmodein;
5237
5238 if (g_fTermcapMode)
5239 return;
5240
5241 SaveConsoleBuffer(&g_cbNonTermcap);
5242
5243 if (g_cbTermcap.IsValid)
5244 {
5245 /*
5246 * We've been in termcap mode before. Restore certain screen
5247 * characteristics, including the buffer size and the window
5248 * size. Since we will be redrawing the screen, we don't need
5249 * to restore the actual contents of the buffer.
5250 */
5251 RestoreConsoleBuffer(&g_cbTermcap, FALSE);
5252 SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow);
5253 Rows = g_cbTermcap.Info.dwSize.Y;
5254 Columns = g_cbTermcap.Info.dwSize.X;
5255 }
5256 else
5257 {
5258 /*
5259 * This is our first time entering termcap mode. Clear the console
5260 * screen buffer, and resize the buffer to match the current window
5261 * size. We will use this as the size of our editing environment.
5262 */
5263 ClearConsoleBuffer(g_attrCurrent);
5264 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
5265 }
5266
5267#ifdef FEAT_TITLE
5268 resettitle();
5269#endif
5270
5271 GetConsoleMode(g_hConIn, &cmodein);
5272#ifdef FEAT_MOUSE
5273 if (g_fMouseActive)
5274 cmodein |= ENABLE_MOUSE_INPUT;
5275 else
5276 cmodein &= ~ENABLE_MOUSE_INPUT;
5277#endif
5278 cmodein |= ENABLE_WINDOW_INPUT;
5279 SetConsoleMode(g_hConIn, cmodein);
5280
5281 redraw_later_clear();
5282 g_fTermcapMode = TRUE;
5283}
5284
5285
5286/*
5287 * End termcap mode
5288 */
5289 static void
5290termcap_mode_end(void)
5291{
5292 DWORD cmodein;
5293 ConsoleBuffer *cb;
5294 COORD coord;
5295 DWORD dwDummy;
5296
5297 if (!g_fTermcapMode)
5298 return;
5299
5300 SaveConsoleBuffer(&g_cbTermcap);
5301
5302 GetConsoleMode(g_hConIn, &cmodein);
5303 cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
5304 SetConsoleMode(g_hConIn, cmodein);
5305
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01005306#ifdef FEAT_RESTORE_ORIG_SCREEN
5307 cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
5308#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005309 cb = &g_cbNonTermcap;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01005310#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005311 RestoreConsoleBuffer(cb, p_rs);
5312 SetConsoleCursorInfo(g_hConOut, &g_cci);
5313
5314 if (p_rs || exiting)
5315 {
5316 /*
5317 * Clear anything that happens to be on the current line.
5318 */
5319 coord.X = 0;
5320 coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1));
5321 FillConsoleOutputCharacter(g_hConOut, ' ',
5322 cb->Info.dwSize.X, coord, &dwDummy);
5323 /*
5324 * The following is just for aesthetics. If we are exiting without
5325 * restoring the screen, then we want to have a prompt string
5326 * appear at the bottom line. However, the command interpreter
5327 * seems to always advance the cursor one line before displaying
5328 * the prompt string, which causes the screen to scroll. To
5329 * counter this, move the cursor up one line before exiting.
5330 */
5331 if (exiting && !p_rs)
5332 coord.Y--;
5333 /*
5334 * Position the cursor at the leftmost column of the desired row.
5335 */
5336 SetConsoleCursorPosition(g_hConOut, coord);
5337 }
5338
5339 g_fTermcapMode = FALSE;
5340}
5341#endif /* FEAT_GUI_W32 */
5342
5343
5344#ifdef FEAT_GUI_W32
5345 void
5346mch_write(
Bram Moolenaar1266d672017-02-01 13:43:36 +01005347 char_u *s UNUSED,
5348 int len UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005349{
5350 /* never used */
5351}
5352
5353#else
5354
5355/*
5356 * clear `n' chars, starting from `coord'
5357 */
5358 static void
5359clear_chars(
5360 COORD coord,
5361 DWORD n)
5362{
5363 DWORD dwDummy;
5364
5365 FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
5366 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy);
5367}
5368
5369
5370/*
5371 * Clear the screen
5372 */
5373 static void
5374clear_screen(void)
5375{
5376 g_coord.X = g_coord.Y = 0;
5377 clear_chars(g_coord, Rows * Columns);
5378}
5379
5380
5381/*
5382 * Clear to end of display
5383 */
5384 static void
5385clear_to_end_of_display(void)
5386{
5387 clear_chars(g_coord, (Rows - g_coord.Y - 1)
5388 * Columns + (Columns - g_coord.X));
5389}
5390
5391
5392/*
5393 * Clear to end of line
5394 */
5395 static void
5396clear_to_end_of_line(void)
5397{
5398 clear_chars(g_coord, Columns - g_coord.X);
5399}
5400
5401
5402/*
5403 * Scroll the scroll region up by `cLines' lines
5404 */
5405 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005406scroll(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005407{
5408 COORD oldcoord = g_coord;
5409
5410 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
5411 delete_lines(cLines);
5412
5413 g_coord = oldcoord;
5414}
5415
5416
5417/*
5418 * Set the scroll region
5419 */
5420 static void
5421set_scroll_region(
5422 unsigned left,
5423 unsigned top,
5424 unsigned right,
5425 unsigned bottom)
5426{
5427 if (left >= right
5428 || top >= bottom
5429 || right > (unsigned) Columns - 1
5430 || bottom > (unsigned) Rows - 1)
5431 return;
5432
5433 g_srScrollRegion.Left = left;
5434 g_srScrollRegion.Top = top;
5435 g_srScrollRegion.Right = right;
5436 g_srScrollRegion.Bottom = bottom;
5437}
5438
5439
5440/*
5441 * Insert `cLines' lines at the current cursor position
5442 */
5443 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005444insert_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005445{
5446 SMALL_RECT source;
5447 COORD dest;
5448 CHAR_INFO fill;
5449
5450 dest.X = 0;
5451 dest.Y = g_coord.Y + cLines;
5452
5453 source.Left = 0;
5454 source.Top = g_coord.Y;
5455 source.Right = g_srScrollRegion.Right;
5456 source.Bottom = g_srScrollRegion.Bottom - cLines;
5457
5458 fill.Char.AsciiChar = ' ';
5459 fill.Attributes = g_attrCurrent;
5460
5461 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
5462
5463 /* Here we have to deal with a win32 console flake: If the scroll
5464 * region looks like abc and we scroll c to a and fill with d we get
5465 * cbd... if we scroll block c one line at a time to a, we get cdd...
5466 * vim expects cdd consistently... So we have to deal with that
5467 * here... (this also occurs scrolling the same way in the other
5468 * direction). */
5469
5470 if (source.Bottom < dest.Y)
5471 {
5472 COORD coord;
5473
5474 coord.X = 0;
5475 coord.Y = source.Bottom;
5476 clear_chars(coord, Columns * (dest.Y - source.Bottom));
5477 }
5478}
5479
5480
5481/*
5482 * Delete `cLines' lines at the current cursor position
5483 */
5484 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005485delete_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005486{
5487 SMALL_RECT source;
5488 COORD dest;
5489 CHAR_INFO fill;
5490 int nb;
5491
5492 dest.X = 0;
5493 dest.Y = g_coord.Y;
5494
5495 source.Left = 0;
5496 source.Top = g_coord.Y + cLines;
5497 source.Right = g_srScrollRegion.Right;
5498 source.Bottom = g_srScrollRegion.Bottom;
5499
5500 fill.Char.AsciiChar = ' ';
5501 fill.Attributes = g_attrCurrent;
5502
5503 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
5504
5505 /* Here we have to deal with a win32 console flake: If the scroll
5506 * region looks like abc and we scroll c to a and fill with d we get
5507 * cbd... if we scroll block c one line at a time to a, we get cdd...
5508 * vim expects cdd consistently... So we have to deal with that
5509 * here... (this also occurs scrolling the same way in the other
5510 * direction). */
5511
5512 nb = dest.Y + (source.Bottom - source.Top) + 1;
5513
5514 if (nb < source.Top)
5515 {
5516 COORD coord;
5517
5518 coord.X = 0;
5519 coord.Y = nb;
5520 clear_chars(coord, Columns * (source.Top - nb));
5521 }
5522}
5523
5524
5525/*
5526 * Set the cursor position
5527 */
5528 static void
5529gotoxy(
5530 unsigned x,
5531 unsigned y)
5532{
5533 if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows)
5534 return;
5535
5536 /* external cursor coords are 1-based; internal are 0-based */
5537 g_coord.X = x - 1;
5538 g_coord.Y = y - 1;
5539 SetConsoleCursorPosition(g_hConOut, g_coord);
5540}
5541
5542
5543/*
5544 * Set the current text attribute = (foreground | background)
5545 * See ../doc/os_win32.txt for the numbers.
5546 */
5547 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005548textattr(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005549{
Bram Moolenaar6383b922015-03-24 17:12:19 +01005550 g_attrCurrent = wAttr & 0xff;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005551
5552 SetConsoleTextAttribute(g_hConOut, wAttr);
5553}
5554
5555
5556 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005557textcolor(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005558{
Bram Moolenaar6383b922015-03-24 17:12:19 +01005559 g_attrCurrent = (g_attrCurrent & 0xf0) + (wAttr & 0x0f);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005560
5561 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
5562}
5563
5564
5565 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005566textbackground(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005567{
Bram Moolenaar6383b922015-03-24 17:12:19 +01005568 g_attrCurrent = (g_attrCurrent & 0x0f) + ((wAttr & 0x0f) << 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005569
5570 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
5571}
5572
5573
5574/*
5575 * restore the default text attribute (whatever we started with)
5576 */
5577 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005578normvideo(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005579{
5580 textattr(g_attrDefault);
5581}
5582
5583
5584static WORD g_attrPreStandout = 0;
5585
5586/*
5587 * Make the text standout, by brightening it
5588 */
5589 static void
5590standout(void)
5591{
5592 g_attrPreStandout = g_attrCurrent;
5593 textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
5594}
5595
5596
5597/*
5598 * Turn off standout mode
5599 */
5600 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005601standend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005602{
5603 if (g_attrPreStandout)
5604 {
5605 textattr(g_attrPreStandout);
5606 g_attrPreStandout = 0;
5607 }
5608}
5609
5610
5611/*
Bram Moolenaarff1d0d42007-05-10 17:24:16 +00005612 * Set normal fg/bg color, based on T_ME. Called when t_me has been set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005613 */
5614 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005615mch_set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005616{
5617 char_u *p;
5618 int n;
5619
5620 cterm_normal_fg_color = (g_attrDefault & 0xf) + 1;
5621 cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1;
5622 if (T_ME[0] == ESC && T_ME[1] == '|')
5623 {
5624 p = T_ME + 2;
5625 n = getdigits(&p);
5626 if (*p == 'm' && n > 0)
5627 {
5628 cterm_normal_fg_color = (n & 0xf) + 1;
5629 cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
5630 }
5631 }
5632}
5633
5634
5635/*
5636 * visual bell: flash the screen
5637 */
5638 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005639visual_bell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005640{
5641 COORD coordOrigin = {0, 0};
5642 WORD attrFlash = ~g_attrCurrent & 0xff;
5643
5644 DWORD dwDummy;
5645 LPWORD oldattrs = (LPWORD)alloc(Rows * Columns * sizeof(WORD));
5646
5647 if (oldattrs == NULL)
5648 return;
5649 ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
5650 coordOrigin, &dwDummy);
5651 FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns,
5652 coordOrigin, &dwDummy);
5653
5654 Sleep(15); /* wait for 15 msec */
5655 WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
5656 coordOrigin, &dwDummy);
5657 vim_free(oldattrs);
5658}
5659
5660
5661/*
5662 * Make the cursor visible or invisible
5663 */
5664 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005665cursor_visible(BOOL fVisible)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005666{
5667 s_cursor_visible = fVisible;
5668#ifdef MCH_CURSOR_SHAPE
5669 mch_update_cursor();
5670#endif
5671}
5672
5673
5674/*
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005675 * write `cbToWrite' bytes in `pchBuf' to the screen
5676 * Returns the number of bytes actually written (at least one).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005677 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005678 static DWORD
Bram Moolenaar071d4272004-06-13 20:20:40 +00005679write_chars(
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005680 char_u *pchBuf,
5681 DWORD cbToWrite)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005682{
5683 COORD coord = g_coord;
5684 DWORD written;
5685
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005686#ifdef FEAT_MBYTE
5687 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
5688 {
5689 static WCHAR *unicodebuf = NULL;
5690 static int unibuflen = 0;
5691 int length;
5692 DWORD n, cchwritten, cells;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693
Bram Moolenaarac360bf2015-09-01 20:31:20 +02005694 length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite, 0, 0);
5695 if (unicodebuf == NULL || length > unibuflen)
5696 {
5697 vim_free(unicodebuf);
5698 unicodebuf = (WCHAR *)lalloc(length * sizeof(WCHAR), FALSE);
5699 unibuflen = length;
5700 }
5701 MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite,
5702 unicodebuf, unibuflen);
5703
5704 cells = mb_string2cells(pchBuf, cbToWrite);
5705 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cells,
5706 coord, &written);
5707 /* When writing fails or didn't write a single character, pretend one
5708 * character was written, otherwise we get stuck. */
5709 if (WriteConsoleOutputCharacterW(g_hConOut, unicodebuf, length,
5710 coord, &cchwritten) == 0
5711 || cchwritten == 0)
5712 cchwritten = 1;
5713
5714 if (cchwritten == length)
5715 {
5716 written = cbToWrite;
5717 g_coord.X += (SHORT)cells;
5718 }
5719 else
5720 {
5721 char_u *p = pchBuf;
5722 for (n = 0; n < cchwritten; n++)
5723 mb_cptr_adv(p);
5724 written = p - pchBuf;
5725 g_coord.X += (SHORT)mb_string2cells(pchBuf, written);
5726 }
5727 }
5728 else
5729#endif
5730 {
5731 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cbToWrite,
5732 coord, &written);
5733 /* When writing fails or didn't write a single character, pretend one
5734 * character was written, otherwise we get stuck. */
5735 if (WriteConsoleOutputCharacter(g_hConOut, (LPCSTR)pchBuf, cbToWrite,
5736 coord, &written) == 0
5737 || written == 0)
5738 written = 1;
5739
5740 g_coord.X += (SHORT) written;
5741 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005742
5743 while (g_coord.X > g_srScrollRegion.Right)
5744 {
5745 g_coord.X -= (SHORT) Columns;
5746 if (g_coord.Y < g_srScrollRegion.Bottom)
5747 g_coord.Y++;
5748 }
5749
5750 gotoxy(g_coord.X + 1, g_coord.Y + 1);
5751
5752 return written;
5753}
5754
5755
5756/*
5757 * mch_write(): write the output buffer to the screen, translating ESC
5758 * sequences into calls to console output routines.
5759 */
5760 void
5761mch_write(
5762 char_u *s,
5763 int len)
5764{
5765 s[len] = NUL;
5766
5767 if (!term_console)
5768 {
5769 write(1, s, (unsigned)len);
5770 return;
5771 }
5772
5773 /* translate ESC | sequences into faked bios calls */
5774 while (len--)
5775 {
5776 /* optimization: use one single write_chars for runs of text,
5777 * rather than once per character It ain't curses, but it helps. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005778 DWORD prefix = (DWORD)strcspn((char *)s, "\n\r\b\a\033");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005779
5780 if (p_wd)
5781 {
5782 WaitForChar(p_wd);
5783 if (prefix != 0)
5784 prefix = 1;
5785 }
5786
5787 if (prefix != 0)
5788 {
5789 DWORD nWritten;
5790
5791 nWritten = write_chars(s, prefix);
5792#ifdef MCH_WRITE_DUMP
5793 if (fdDump)
5794 {
5795 fputc('>', fdDump);
5796 fwrite(s, sizeof(char_u), nWritten, fdDump);
5797 fputs("<\n", fdDump);
5798 }
5799#endif
5800 len -= (nWritten - 1);
5801 s += nWritten;
5802 }
5803 else if (s[0] == '\n')
5804 {
5805 /* \n, newline: go to the beginning of the next line or scroll */
5806 if (g_coord.Y == g_srScrollRegion.Bottom)
5807 {
5808 scroll(1);
5809 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
5810 }
5811 else
5812 {
5813 gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
5814 }
5815#ifdef MCH_WRITE_DUMP
5816 if (fdDump)
5817 fputs("\\n\n", fdDump);
5818#endif
5819 s++;
5820 }
5821 else if (s[0] == '\r')
5822 {
5823 /* \r, carriage return: go to beginning of line */
5824 gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
5825#ifdef MCH_WRITE_DUMP
5826 if (fdDump)
5827 fputs("\\r\n", fdDump);
5828#endif
5829 s++;
5830 }
5831 else if (s[0] == '\b')
5832 {
5833 /* \b, backspace: move cursor one position left */
5834 if (g_coord.X > g_srScrollRegion.Left)
5835 g_coord.X--;
5836 else if (g_coord.Y > g_srScrollRegion.Top)
5837 {
5838 g_coord.X = g_srScrollRegion.Right;
5839 g_coord.Y--;
5840 }
5841 gotoxy(g_coord.X + 1, g_coord.Y + 1);
5842#ifdef MCH_WRITE_DUMP
5843 if (fdDump)
5844 fputs("\\b\n", fdDump);
5845#endif
5846 s++;
5847 }
5848 else if (s[0] == '\a')
5849 {
5850 /* \a, bell */
5851 MessageBeep(0xFFFFFFFF);
5852#ifdef MCH_WRITE_DUMP
5853 if (fdDump)
5854 fputs("\\a\n", fdDump);
5855#endif
5856 s++;
5857 }
5858 else if (s[0] == ESC && len >= 3-1 && s[1] == '|')
5859 {
5860#ifdef MCH_WRITE_DUMP
Bram Moolenaarc0197e22004-09-13 20:26:32 +00005861 char_u *old_s = s;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005862#endif
Bram Moolenaarc0197e22004-09-13 20:26:32 +00005863 char_u *p;
5864 int arg1 = 0, arg2 = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005865
5866 switch (s[2])
5867 {
5868 /* one or two numeric arguments, separated by ';' */
5869
5870 case '0': case '1': case '2': case '3': case '4':
5871 case '5': case '6': case '7': case '8': case '9':
5872 p = s + 2;
5873 arg1 = getdigits(&p); /* no check for length! */
5874 if (p > s + len)
5875 break;
5876
5877 if (*p == ';')
5878 {
5879 ++p;
5880 arg2 = getdigits(&p); /* no check for length! */
5881 if (p > s + len)
5882 break;
5883
5884 if (*p == 'H')
5885 gotoxy(arg2, arg1);
5886 else if (*p == 'r')
5887 set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
5888 }
5889 else if (*p == 'A')
5890 {
5891 /* move cursor up arg1 lines in same column */
5892 gotoxy(g_coord.X + 1,
5893 max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
5894 }
5895 else if (*p == 'C')
5896 {
5897 /* move cursor right arg1 columns in same line */
5898 gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
5899 g_coord.Y + 1);
5900 }
5901 else if (*p == 'H')
5902 {
5903 gotoxy(1, arg1);
5904 }
5905 else if (*p == 'L')
5906 {
5907 insert_lines(arg1);
5908 }
5909 else if (*p == 'm')
5910 {
5911 if (arg1 == 0)
5912 normvideo();
5913 else
5914 textattr((WORD) arg1);
5915 }
5916 else if (*p == 'f')
5917 {
5918 textcolor((WORD) arg1);
5919 }
5920 else if (*p == 'b')
5921 {
5922 textbackground((WORD) arg1);
5923 }
5924 else if (*p == 'M')
5925 {
5926 delete_lines(arg1);
5927 }
5928
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00005929 len -= (int)(p - s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005930 s = p + 1;
5931 break;
5932
5933
5934 /* Three-character escape sequences */
5935
5936 case 'A':
5937 /* move cursor up one line in same column */
5938 gotoxy(g_coord.X + 1,
5939 max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
5940 goto got3;
5941
5942 case 'B':
5943 visual_bell();
5944 goto got3;
5945
5946 case 'C':
5947 /* move cursor right one column in same line */
5948 gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
5949 g_coord.Y + 1);
5950 goto got3;
5951
5952 case 'E':
5953 termcap_mode_end();
5954 goto got3;
5955
5956 case 'F':
5957 standout();
5958 goto got3;
5959
5960 case 'f':
5961 standend();
5962 goto got3;
5963
5964 case 'H':
5965 gotoxy(1, 1);
5966 goto got3;
5967
5968 case 'j':
5969 clear_to_end_of_display();
5970 goto got3;
5971
5972 case 'J':
5973 clear_screen();
5974 goto got3;
5975
5976 case 'K':
5977 clear_to_end_of_line();
5978 goto got3;
5979
5980 case 'L':
5981 insert_lines(1);
5982 goto got3;
5983
5984 case 'M':
5985 delete_lines(1);
5986 goto got3;
5987
5988 case 'S':
5989 termcap_mode_start();
5990 goto got3;
5991
5992 case 'V':
5993 cursor_visible(TRUE);
5994 goto got3;
5995
5996 case 'v':
5997 cursor_visible(FALSE);
5998 goto got3;
5999
6000 got3:
6001 s += 3;
6002 len -= 2;
6003 }
6004
6005#ifdef MCH_WRITE_DUMP
6006 if (fdDump)
6007 {
6008 fputs("ESC | ", fdDump);
6009 fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
6010 fputc('\n', fdDump);
6011 }
6012#endif
6013 }
6014 else
6015 {
6016 /* Write a single character */
6017 DWORD nWritten;
6018
6019 nWritten = write_chars(s, 1);
6020#ifdef MCH_WRITE_DUMP
6021 if (fdDump)
6022 {
6023 fputc('>', fdDump);
6024 fwrite(s, sizeof(char_u), nWritten, fdDump);
6025 fputs("<\n", fdDump);
6026 }
6027#endif
6028
6029 len -= (nWritten - 1);
6030 s += nWritten;
6031 }
6032 }
6033
6034#ifdef MCH_WRITE_DUMP
6035 if (fdDump)
6036 fflush(fdDump);
6037#endif
6038}
6039
6040#endif /* FEAT_GUI_W32 */
6041
6042
6043/*
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01006044 * Delay for "msec" milliseconds.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006045 */
6046 void
6047mch_delay(
6048 long msec,
Bram Moolenaar1266d672017-02-01 13:43:36 +01006049 int ignoreinput UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006050{
6051#ifdef FEAT_GUI_W32
6052 Sleep((int)msec); /* never wait for input */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006053#else /* Console */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006054 if (ignoreinput)
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006055# ifdef FEAT_MZSCHEME
6056 if (mzthreads_allowed() && p_mzq > 0 && msec > p_mzq)
6057 {
6058 int towait = p_mzq;
6059
6060 /* if msec is large enough, wait by portions in p_mzq */
6061 while (msec > 0)
6062 {
6063 mzvim_check_threads();
6064 if (msec < towait)
6065 towait = msec;
6066 Sleep(towait);
6067 msec -= towait;
6068 }
6069 }
6070 else
6071# endif
6072 Sleep((int)msec);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006073 else
6074 WaitForChar(msec);
6075#endif
6076}
6077
6078
6079/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01006080 * This version of remove is not scared by a readonly (backup) file.
6081 * This can also remove a symbolic link like Unix.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006082 * Return 0 for success, -1 for failure.
6083 */
6084 int
6085mch_remove(char_u *name)
6086{
6087#ifdef FEAT_MBYTE
6088 WCHAR *wn = NULL;
6089 int n;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02006090#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006091
Bram Moolenaar203258c2016-01-17 22:15:16 +01006092 /*
6093 * On Windows, deleting a directory's symbolic link is done by
6094 * RemoveDirectory(): mch_rmdir. It seems unnatural, but it is fact.
6095 */
6096 if (mch_isdir(name) && mch_is_symbolic_link(name))
6097 return mch_rmdir(name);
6098
Bram Moolenaar12b559e2013-06-12 22:41:37 +02006099 win32_setattrs(name, FILE_ATTRIBUTE_NORMAL);
6100
6101#ifdef FEAT_MBYTE
Bram Moolenaar071d4272004-06-13 20:20:40 +00006102 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6103 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006104 wn = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006105 if (wn != NULL)
6106 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006107 n = DeleteFileW(wn) ? 0 : -1;
6108 vim_free(wn);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006109 return n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006110 }
6111 }
6112#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006113 return DeleteFile((LPCSTR)name) ? 0 : -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006114}
6115
6116
6117/*
Bram Moolenaarb9c31e72016-09-29 15:18:57 +02006118 * Check for an "interrupt signal": CTRL-break or CTRL-C.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006119 */
6120 void
Bram Moolenaarb9c31e72016-09-29 15:18:57 +02006121mch_breakcheck(int force)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006122{
6123#ifndef FEAT_GUI_W32 /* never used */
6124 if (g_fCtrlCPressed || g_fCBrkPressed)
6125 {
6126 g_fCtrlCPressed = g_fCBrkPressed = FALSE;
6127 got_int = TRUE;
6128 }
6129#endif
6130}
6131
Bram Moolenaaree273972016-01-02 21:11:51 +01006132/* physical RAM to leave for the OS */
6133#define WINNT_RESERVE_BYTES (256*1024*1024)
Bram Moolenaaree273972016-01-02 21:11:51 +01006134
6135/*
6136 * How much main memory in KiB that can be used by VIM.
6137 */
Bram Moolenaaree273972016-01-02 21:11:51 +01006138 long_u
Bram Moolenaar1266d672017-02-01 13:43:36 +01006139mch_total_mem(int special UNUSED)
Bram Moolenaaree273972016-01-02 21:11:51 +01006140{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006141 MEMORYSTATUSEX ms;
6142
Bram Moolenaaree273972016-01-02 21:11:51 +01006143 PlatformId();
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006144 /* Need to use GlobalMemoryStatusEx() when there is more memory than
6145 * what fits in 32 bits. But it's not always available. */
6146 ms.dwLength = sizeof(MEMORYSTATUSEX);
6147 GlobalMemoryStatusEx(&ms);
6148 if (ms.ullAvailVirtual < ms.ullTotalPhys)
Bram Moolenaaree273972016-01-02 21:11:51 +01006149 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006150 /* Process address space fits in physical RAM, use all of it. */
6151 return (long_u)(ms.ullAvailVirtual / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01006152 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006153 if (ms.ullTotalPhys <= WINNT_RESERVE_BYTES)
Bram Moolenaaree273972016-01-02 21:11:51 +01006154 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006155 /* Catch old NT box or perverse hardware setup. */
6156 return (long_u)((ms.ullTotalPhys / 2) / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01006157 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006158 /* Use physical RAM less reserve for OS + data. */
6159 return (long_u)((ms.ullTotalPhys - WINNT_RESERVE_BYTES) / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01006160}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006161
Bram Moolenaar071d4272004-06-13 20:20:40 +00006162#ifdef FEAT_MBYTE
6163/*
6164 * Same code as below, but with wide functions and no comments.
6165 * Return 0 for success, non-zero for failure.
6166 */
6167 int
6168mch_wrename(WCHAR *wold, WCHAR *wnew)
6169{
6170 WCHAR *p;
6171 int i;
6172 WCHAR szTempFile[_MAX_PATH + 1];
6173 WCHAR szNewPath[_MAX_PATH + 1];
6174 HANDLE hf;
6175
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006176 p = wold;
6177 for (i = 0; wold[i] != NUL; ++i)
6178 if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':')
6179 && wold[i + 1] != 0)
6180 p = wold + i + 1;
6181 if ((int)(wold + i - p) < 8 || p[6] != '~')
6182 return (MoveFileW(wold, wnew) == 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006183
6184 if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL)
6185 return -1;
6186 *p = NUL;
6187
6188 if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0)
6189 return -2;
6190
6191 if (!DeleteFileW(szTempFile))
6192 return -3;
6193
6194 if (!MoveFileW(wold, szTempFile))
6195 return -4;
6196
6197 if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW,
6198 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
6199 return -5;
6200 if (!CloseHandle(hf))
6201 return -6;
6202
6203 if (!MoveFileW(szTempFile, wnew))
6204 {
6205 (void)MoveFileW(szTempFile, wold);
6206 return -7;
6207 }
6208
6209 DeleteFileW(szTempFile);
6210
6211 if (!DeleteFileW(wold))
6212 return -8;
6213
6214 return 0;
6215}
6216#endif
6217
6218
6219/*
6220 * mch_rename() works around a bug in rename (aka MoveFile) in
6221 * Windows 95: rename("foo.bar", "foo.bar~") will generate a
6222 * file whose short file name is "FOO.BAR" (its long file name will
6223 * be correct: "foo.bar~"). Because a file can be accessed by
6224 * either its SFN or its LFN, "foo.bar" has effectively been
6225 * renamed to "foo.bar", which is not at all what was wanted. This
6226 * seems to happen only when renaming files with three-character
6227 * extensions by appending a suffix that does not include ".".
6228 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
6229 *
6230 * There is another problem, which isn't really a bug but isn't right either:
6231 * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be
6232 * "abcdef~1.txt" again. This has been reported on Windows NT 4.0 with
6233 * service pack 6. Doesn't seem to happen on Windows 98.
6234 *
6235 * Like rename(), returns 0 upon success, non-zero upon failure.
6236 * Should probably set errno appropriately when errors occur.
6237 */
6238 int
6239mch_rename(
6240 const char *pszOldFile,
6241 const char *pszNewFile)
6242{
6243 char szTempFile[_MAX_PATH+1];
6244 char szNewPath[_MAX_PATH+1];
6245 char *pszFilePart;
6246 HANDLE hf;
6247#ifdef FEAT_MBYTE
6248 WCHAR *wold = NULL;
6249 WCHAR *wnew = NULL;
6250 int retval = -1;
6251
6252 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6253 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006254 wold = enc_to_utf16((char_u *)pszOldFile, NULL);
6255 wnew = enc_to_utf16((char_u *)pszNewFile, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006256 if (wold != NULL && wnew != NULL)
6257 retval = mch_wrename(wold, wnew);
6258 vim_free(wold);
6259 vim_free(wnew);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006260 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006261 }
6262#endif
6263
6264 /*
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006265 * No need to play tricks unless the file name contains a "~" as the
6266 * seventh character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006267 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006268 pszFilePart = (char *)gettail((char_u *)pszOldFile);
6269 if (STRLEN(pszFilePart) < 8 || pszFilePart[6] != '~')
6270 return rename(pszOldFile, pszNewFile);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006271
6272 /* Get base path of new file name. Undocumented feature: If pszNewFile is
6273 * a directory, no error is returned and pszFilePart will be NULL. */
6274 if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0
6275 || pszFilePart == NULL)
6276 return -1;
6277 *pszFilePart = NUL;
6278
6279 /* Get (and create) a unique temporary file name in directory of new file */
6280 if (GetTempFileName(szNewPath, "VIM", 0, szTempFile) == 0)
6281 return -2;
6282
6283 /* blow the temp file away */
6284 if (!DeleteFile(szTempFile))
6285 return -3;
6286
6287 /* rename old file to the temp file */
6288 if (!MoveFile(pszOldFile, szTempFile))
6289 return -4;
6290
6291 /* now create an empty file called pszOldFile; this prevents the operating
6292 * system using pszOldFile as an alias (SFN) if we're renaming within the
6293 * same directory. For example, we're editing a file called
6294 * filename.asc.txt by its SFN, filena~1.txt. If we rename filena~1.txt
6295 * to filena~1.txt~ (i.e., we're making a backup while writing it), the
6296 * SFN for filena~1.txt~ will be filena~1.txt, by default, which will
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006297 * cause all sorts of problems later in buf_write(). So, we create an
6298 * empty file called filena~1.txt and the system will have to find some
6299 * other SFN for filena~1.txt~, such as filena~2.txt
Bram Moolenaar071d4272004-06-13 20:20:40 +00006300 */
6301 if ((hf = CreateFile(pszOldFile, GENERIC_WRITE, 0, NULL, CREATE_NEW,
6302 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
6303 return -5;
6304 if (!CloseHandle(hf))
6305 return -6;
6306
6307 /* rename the temp file to the new file */
6308 if (!MoveFile(szTempFile, pszNewFile))
6309 {
6310 /* Renaming failed. Rename the file back to its old name, so that it
6311 * looks like nothing happened. */
6312 (void)MoveFile(szTempFile, pszOldFile);
6313
6314 return -7;
6315 }
6316
6317 /* Seems to be left around on Novell filesystems */
6318 DeleteFile(szTempFile);
6319
6320 /* finally, remove the empty old file */
6321 if (!DeleteFile(pszOldFile))
6322 return -8;
6323
6324 return 0; /* success */
6325}
6326
6327/*
6328 * Get the default shell for the current hardware platform
6329 */
6330 char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006331default_shell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006332{
Bram Moolenaar071d4272004-06-13 20:20:40 +00006333 PlatformId();
6334
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006335 return "cmd.exe";
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336}
6337
6338/*
6339 * mch_access() extends access() to do more detailed check on network drives.
6340 * Returns 0 if file "n" has access rights according to "p", -1 otherwise.
6341 */
6342 int
6343mch_access(char *n, int p)
6344{
6345 HANDLE hFile;
6346 DWORD am;
6347 int retval = -1; /* default: fail */
6348#ifdef FEAT_MBYTE
6349 WCHAR *wn = NULL;
6350
6351 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006352 wn = enc_to_utf16((char_u *)n, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006353#endif
6354
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006355 if (mch_isdir((char_u *)n))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006356 {
6357 char TempName[_MAX_PATH + 16] = "";
6358#ifdef FEAT_MBYTE
6359 WCHAR TempNameW[_MAX_PATH + 16] = L"";
6360#endif
6361
6362 if (p & R_OK)
6363 {
6364 /* Read check is performed by seeing if we can do a find file on
6365 * the directory for any file. */
6366#ifdef FEAT_MBYTE
6367 if (wn != NULL)
6368 {
6369 int i;
6370 WIN32_FIND_DATAW d;
6371
6372 for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i)
6373 TempNameW[i] = wn[i];
6374 if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/')
6375 TempNameW[i++] = '\\';
6376 TempNameW[i++] = '*';
6377 TempNameW[i++] = 0;
6378
6379 hFile = FindFirstFileW(TempNameW, &d);
6380 if (hFile == INVALID_HANDLE_VALUE)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006381 goto getout;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006382 else
6383 (void)FindClose(hFile);
6384 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006385 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006386#endif
6387 {
6388 char *pch;
6389 WIN32_FIND_DATA d;
6390
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006391 vim_strncpy((char_u *)TempName, (char_u *)n, _MAX_PATH);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006392 pch = TempName + STRLEN(TempName) - 1;
6393 if (*pch != '\\' && *pch != '/')
6394 *++pch = '\\';
6395 *++pch = '*';
6396 *++pch = NUL;
6397
6398 hFile = FindFirstFile(TempName, &d);
6399 if (hFile == INVALID_HANDLE_VALUE)
6400 goto getout;
6401 (void)FindClose(hFile);
6402 }
6403 }
6404
6405 if (p & W_OK)
6406 {
6407 /* Trying to create a temporary file in the directory should catch
6408 * directories on read-only network shares. However, in
6409 * directories whose ACL allows writes but denies deletes will end
6410 * up keeping the temporary file :-(. */
6411#ifdef FEAT_MBYTE
6412 if (wn != NULL)
6413 {
6414 if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW))
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006415 goto getout;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006416 else
6417 DeleteFileW(TempNameW);
6418 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006419 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006420#endif
6421 {
6422 if (!GetTempFileName(n, "VIM", 0, TempName))
6423 goto getout;
6424 mch_remove((char_u *)TempName);
6425 }
6426 }
6427 }
6428 else
6429 {
6430 /* Trying to open the file for the required access does ACL, read-only
6431 * network share, and file attribute checks. */
6432 am = ((p & W_OK) ? GENERIC_WRITE : 0)
6433 | ((p & R_OK) ? GENERIC_READ : 0);
6434#ifdef FEAT_MBYTE
6435 if (wn != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006436 hFile = CreateFileW(wn, am, 0, NULL, OPEN_EXISTING, 0, NULL);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006437 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006438#endif
6439 hFile = CreateFile(n, am, 0, NULL, OPEN_EXISTING, 0, NULL);
6440 if (hFile == INVALID_HANDLE_VALUE)
6441 goto getout;
6442 CloseHandle(hFile);
6443 }
6444
6445 retval = 0; /* success */
6446getout:
6447#ifdef FEAT_MBYTE
6448 vim_free(wn);
6449#endif
6450 return retval;
6451}
6452
6453#if defined(FEAT_MBYTE) || defined(PROTO)
6454/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006455 * Version of open() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006456 */
6457 int
6458mch_open(char *name, int flags, int mode)
6459{
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006460 /* _wopen() does not work with Borland C 5.5: creates a read-only file. */
6461# ifndef __BORLANDC__
Bram Moolenaar071d4272004-06-13 20:20:40 +00006462 WCHAR *wn;
6463 int f;
6464
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006465 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006466 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006467 wn = enc_to_utf16((char_u *)name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006468 if (wn != NULL)
6469 {
6470 f = _wopen(wn, flags, mode);
6471 vim_free(wn);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006472 return f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006473 }
6474 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00006475# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006476
Bram Moolenaarf9e6c3b2014-11-05 18:36:03 +01006477 /* open() can open a file which name is longer than _MAX_PATH bytes
6478 * and shorter than _MAX_PATH characters successfully, but sometimes it
6479 * causes unexpected error in another part. We make it an error explicitly
6480 * here. */
6481 if (strlen(name) >= _MAX_PATH)
6482 return -1;
6483
Bram Moolenaar071d4272004-06-13 20:20:40 +00006484 return open(name, flags, mode);
6485}
6486
6487/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006488 * Version of fopen() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006489 */
6490 FILE *
6491mch_fopen(char *name, char *mode)
6492{
6493 WCHAR *wn, *wm;
6494 FILE *f = NULL;
6495
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006496 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006497 {
Bram Moolenaare6a91fd2008-07-24 18:51:11 +00006498# if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0fde2902008-03-16 13:54:13 +00006499 /* Work around an annoying assertion in the Microsoft debug CRT
6500 * when mode's text/binary setting doesn't match _get_fmode(). */
6501 char newMode = mode[strlen(mode) - 1];
6502 int oldMode = 0;
6503
6504 _get_fmode(&oldMode);
6505 if (newMode == 't')
6506 _set_fmode(_O_TEXT);
6507 else if (newMode == 'b')
6508 _set_fmode(_O_BINARY);
6509# endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01006510 wn = enc_to_utf16((char_u *)name, NULL);
6511 wm = enc_to_utf16((char_u *)mode, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006512 if (wn != NULL && wm != NULL)
6513 f = _wfopen(wn, wm);
6514 vim_free(wn);
6515 vim_free(wm);
Bram Moolenaar0fde2902008-03-16 13:54:13 +00006516
Bram Moolenaare6a91fd2008-07-24 18:51:11 +00006517# if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0fde2902008-03-16 13:54:13 +00006518 _set_fmode(oldMode);
6519# endif
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006520 return f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006521 }
6522
Bram Moolenaarf9e6c3b2014-11-05 18:36:03 +01006523 /* fopen() can open a file which name is longer than _MAX_PATH bytes
6524 * and shorter than _MAX_PATH characters successfully, but sometimes it
6525 * causes unexpected error in another part. We make it an error explicitly
6526 * here. */
6527 if (strlen(name) >= _MAX_PATH)
6528 return NULL;
6529
Bram Moolenaar071d4272004-06-13 20:20:40 +00006530 return fopen(name, mode);
6531}
6532#endif
6533
6534#ifdef FEAT_MBYTE
6535/*
6536 * SUB STREAM (aka info stream) handling:
6537 *
6538 * NTFS can have sub streams for each file. Normal contents of file is
6539 * stored in the main stream, and extra contents (author information and
6540 * title and so on) can be stored in sub stream. After Windows 2000, user
6541 * can access and store those informations in sub streams via explorer's
6542 * property menuitem in right click menu. Those informations in sub streams
6543 * were lost when copying only the main stream. So we have to copy sub
6544 * streams.
6545 *
6546 * Incomplete explanation:
6547 * http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp
6548 * More useful info and an example:
6549 * http://www.sysinternals.com/ntw2k/source/misc.shtml#streams
6550 */
6551
6552/*
6553 * Copy info stream data "substream". Read from the file with BackupRead(sh)
6554 * and write to stream "substream" of file "to".
6555 * Errors are ignored.
6556 */
6557 static void
6558copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len)
6559{
6560 HANDLE hTo;
6561 WCHAR *to_name;
6562
6563 to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR));
6564 wcscpy(to_name, to);
6565 wcscat(to_name, substream);
6566
6567 hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
6568 FILE_ATTRIBUTE_NORMAL, NULL);
6569 if (hTo != INVALID_HANDLE_VALUE)
6570 {
6571 long done;
6572 DWORD todo;
6573 DWORD readcnt, written;
6574 char buf[4096];
6575
6576 /* Copy block of bytes at a time. Abort when something goes wrong. */
6577 for (done = 0; done < len; done += written)
6578 {
6579 /* (size_t) cast for Borland C 5.5 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00006580 todo = (DWORD)((size_t)(len - done) > sizeof(buf) ? sizeof(buf)
6581 : (size_t)(len - done));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006582 if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt,
6583 FALSE, FALSE, context)
6584 || readcnt != todo
6585 || !WriteFile(hTo, buf, todo, &written, NULL)
6586 || written != todo)
6587 break;
6588 }
6589 CloseHandle(hTo);
6590 }
6591
6592 free(to_name);
6593}
6594
6595/*
6596 * Copy info streams from file "from" to file "to".
6597 */
6598 static void
6599copy_infostreams(char_u *from, char_u *to)
6600{
6601 WCHAR *fromw;
6602 WCHAR *tow;
6603 HANDLE sh;
6604 WIN32_STREAM_ID sid;
6605 int headersize;
6606 WCHAR streamname[_MAX_PATH];
6607 DWORD readcount;
6608 void *context = NULL;
6609 DWORD lo, hi;
6610 int len;
6611
6612 /* Convert the file names to wide characters. */
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006613 fromw = enc_to_utf16(from, NULL);
6614 tow = enc_to_utf16(to, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006615 if (fromw != NULL && tow != NULL)
6616 {
6617 /* Open the file for reading. */
6618 sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL,
6619 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
6620 if (sh != INVALID_HANDLE_VALUE)
6621 {
6622 /* Use BackupRead() to find the info streams. Repeat until we
6623 * have done them all.*/
6624 for (;;)
6625 {
6626 /* Get the header to find the length of the stream name. If
6627 * the "readcount" is zero we have done all info streams. */
6628 ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00006629 headersize = (int)((char *)&sid.cStreamName - (char *)&sid.dwStreamId);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006630 if (!BackupRead(sh, (LPBYTE)&sid, headersize,
6631 &readcount, FALSE, FALSE, &context)
6632 || readcount == 0)
6633 break;
6634
6635 /* We only deal with streams that have a name. The normal
6636 * file data appears to be without a name, even though docs
6637 * suggest it is called "::$DATA". */
6638 if (sid.dwStreamNameSize > 0)
6639 {
6640 /* Read the stream name. */
6641 if (!BackupRead(sh, (LPBYTE)streamname,
6642 sid.dwStreamNameSize,
6643 &readcount, FALSE, FALSE, &context))
6644 break;
6645
6646 /* Copy an info stream with a name ":anything:$DATA".
6647 * Skip "::$DATA", it has no stream name (examples suggest
6648 * it might be used for the normal file contents).
6649 * Note that BackupRead() counts bytes, but the name is in
6650 * wide characters. */
6651 len = readcount / sizeof(WCHAR);
6652 streamname[len] = 0;
6653 if (len > 7 && wcsicmp(streamname + len - 6,
6654 L":$DATA") == 0)
6655 {
6656 streamname[len - 6] = 0;
6657 copy_substream(sh, &context, tow, streamname,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00006658 (long)sid.Size.u.LowPart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006659 }
6660 }
6661
6662 /* Advance to the next stream. We might try seeking too far,
6663 * but BackupSeek() doesn't skip over stream borders, thus
6664 * that's OK. */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00006665 (void)BackupSeek(sh, sid.Size.u.LowPart, sid.Size.u.HighPart,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006666 &lo, &hi, &context);
6667 }
6668
6669 /* Clear the context. */
6670 (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context);
6671
6672 CloseHandle(sh);
6673 }
6674 }
6675 vim_free(fromw);
6676 vim_free(tow);
6677}
6678#endif
6679
6680/*
6681 * Copy file attributes from file "from" to file "to".
6682 * For Windows NT and later we copy info streams.
6683 * Always returns zero, errors are ignored.
6684 */
6685 int
6686mch_copy_file_attribute(char_u *from, char_u *to)
6687{
6688#ifdef FEAT_MBYTE
6689 /* File streams only work on Windows NT and later. */
6690 PlatformId();
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006691 copy_infostreams(from, to);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006692#endif
6693 return 0;
6694}
6695
6696#if defined(MYRESETSTKOFLW) || defined(PROTO)
6697/*
6698 * Recreate a destroyed stack guard page in win32.
6699 * Written by Benjamin Peterson.
6700 */
6701
6702/* These magic numbers are from the MS header files */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006703#define MIN_STACK_WINNT 2
6704
6705/*
6706 * This function does the same thing as _resetstkoflw(), which is only
6707 * available in DevStudio .net and later.
6708 * Returns 0 for failure, 1 for success.
6709 */
6710 int
6711myresetstkoflw(void)
6712{
6713 BYTE *pStackPtr;
6714 BYTE *pGuardPage;
6715 BYTE *pStackBase;
6716 BYTE *pLowestPossiblePage;
6717 MEMORY_BASIC_INFORMATION mbi;
6718 SYSTEM_INFO si;
6719 DWORD nPageSize;
6720 DWORD dummy;
6721
Bram Moolenaar071d4272004-06-13 20:20:40 +00006722 PlatformId();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006723
6724 /* We need to know the system page size. */
6725 GetSystemInfo(&si);
6726 nPageSize = si.dwPageSize;
6727
6728 /* ...and the current stack pointer */
6729 pStackPtr = (BYTE*)_alloca(1);
6730
6731 /* ...and the base of the stack. */
6732 if (VirtualQuery(pStackPtr, &mbi, sizeof mbi) == 0)
6733 return 0;
6734 pStackBase = (BYTE*)mbi.AllocationBase;
6735
6736 /* ...and the page thats min_stack_req pages away from stack base; this is
6737 * the lowest page we could use. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006738 pLowestPossiblePage = pStackBase + MIN_STACK_WINNT * nPageSize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006739
Bram Moolenaar071d4272004-06-13 20:20:40 +00006740 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006741 /* We want the first committed page in the stack Start at the stack
6742 * base and move forward through memory until we find a committed block.
6743 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006744 BYTE *pBlock = pStackBase;
6745
Bram Moolenaara466c992005-07-09 21:03:22 +00006746 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006747 {
6748 if (VirtualQuery(pBlock, &mbi, sizeof mbi) == 0)
6749 return 0;
6750
6751 pBlock += mbi.RegionSize;
6752
6753 if (mbi.State & MEM_COMMIT)
6754 break;
6755 }
6756
6757 /* mbi now describes the first committed block in the stack. */
6758 if (mbi.Protect & PAGE_GUARD)
6759 return 1;
6760
6761 /* decide where the guard page should start */
6762 if ((long_u)(mbi.BaseAddress) < (long_u)pLowestPossiblePage)
6763 pGuardPage = pLowestPossiblePage;
6764 else
6765 pGuardPage = (BYTE*)mbi.BaseAddress;
6766
6767 /* allocate the guard page */
6768 if (!VirtualAlloc(pGuardPage, nPageSize, MEM_COMMIT, PAGE_READWRITE))
6769 return 0;
6770
6771 /* apply the guard attribute to the page */
6772 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_READWRITE | PAGE_GUARD,
6773 &dummy))
6774 return 0;
6775 }
6776
6777 return 1;
6778}
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006779#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006780
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006781
6782#if defined(FEAT_MBYTE) || defined(PROTO)
6783/*
6784 * The command line arguments in UCS2
6785 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00006786static int nArgsW = 0;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006787static LPWSTR *ArglistW = NULL;
6788static int global_argc = 0;
6789static char **global_argv;
6790
6791static int used_file_argc = 0; /* last argument in global_argv[] used
6792 for the argument list. */
6793static int *used_file_indexes = NULL; /* indexes in global_argv[] for
6794 command line arguments added to
6795 the argument list */
6796static int used_file_count = 0; /* nr of entries in used_file_indexes */
6797static int used_file_literal = FALSE; /* take file names literally */
6798static int used_file_full_path = FALSE; /* file name was full path */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006799static int used_file_diff_mode = FALSE; /* file name was with diff mode */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006800static int used_alist_count = 0;
6801
6802
6803/*
6804 * Get the command line arguments. Unicode version.
6805 * Returns argc. Zero when something fails.
6806 */
6807 int
6808get_cmd_argsW(char ***argvp)
6809{
6810 char **argv = NULL;
6811 int argc = 0;
6812 int i;
6813
Bram Moolenaar14993322014-09-09 12:25:33 +02006814 free_cmd_argsW();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006815 ArglistW = CommandLineToArgvW(GetCommandLineW(), &nArgsW);
6816 if (ArglistW != NULL)
6817 {
6818 argv = malloc((nArgsW + 1) * sizeof(char *));
6819 if (argv != NULL)
6820 {
6821 argc = nArgsW;
6822 argv[argc] = NULL;
6823 for (i = 0; i < argc; ++i)
6824 {
6825 int len;
6826
6827 /* Convert each Unicode argument to the current codepage. */
6828 WideCharToMultiByte_alloc(GetACP(), 0,
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00006829 ArglistW[i], (int)wcslen(ArglistW[i]) + 1,
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006830 (LPSTR *)&argv[i], &len, 0, 0);
6831 if (argv[i] == NULL)
6832 {
6833 /* Out of memory, clear everything. */
6834 while (i > 0)
6835 free(argv[--i]);
6836 free(argv);
Bram Moolenaar73c61632013-12-07 14:48:10 +01006837 argv = NULL;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006838 argc = 0;
6839 }
6840 }
6841 }
6842 }
6843
6844 global_argc = argc;
6845 global_argv = argv;
6846 if (argc > 0)
Bram Moolenaar14993322014-09-09 12:25:33 +02006847 {
6848 if (used_file_indexes != NULL)
6849 free(used_file_indexes);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006850 used_file_indexes = malloc(argc * sizeof(int));
Bram Moolenaar14993322014-09-09 12:25:33 +02006851 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006852
6853 if (argvp != NULL)
6854 *argvp = argv;
6855 return argc;
6856}
6857
6858 void
6859free_cmd_argsW(void)
6860{
6861 if (ArglistW != NULL)
6862 {
6863 GlobalFree(ArglistW);
6864 ArglistW = NULL;
6865 }
6866}
6867
6868/*
6869 * Remember "name" is an argument that was added to the argument list.
6870 * This avoids that we have to re-parse the argument list when fix_arg_enc()
6871 * is called.
6872 */
6873 void
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006874used_file_arg(char *name, int literal, int full_path, int diff_mode)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006875{
6876 int i;
6877
6878 if (used_file_indexes == NULL)
6879 return;
6880 for (i = used_file_argc + 1; i < global_argc; ++i)
6881 if (STRCMP(global_argv[i], name) == 0)
6882 {
6883 used_file_argc = i;
6884 used_file_indexes[used_file_count++] = i;
6885 break;
6886 }
6887 used_file_literal = literal;
6888 used_file_full_path = full_path;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006889 used_file_diff_mode = diff_mode;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006890}
6891
6892/*
6893 * Remember the length of the argument list as it was. If it changes then we
6894 * leave it alone when 'encoding' is set.
6895 */
6896 void
6897set_alist_count(void)
6898{
6899 used_alist_count = GARGCOUNT;
6900}
6901
6902/*
6903 * Fix the encoding of the command line arguments. Invoked when 'encoding'
6904 * has been changed while starting up. Use the UCS-2 command line arguments
6905 * and convert them to 'encoding'.
6906 */
6907 void
6908fix_arg_enc(void)
6909{
6910 int i;
6911 int idx;
6912 char_u *str;
Bram Moolenaar86b68352004-12-27 21:59:20 +00006913 int *fnum_list;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006914
6915 /* Safety checks:
6916 * - if argument count differs between the wide and non-wide argument
6917 * list, something must be wrong.
6918 * - the file name arguments must have been located.
6919 * - the length of the argument list wasn't changed by the user.
6920 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00006921 if (global_argc != nArgsW
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006922 || ArglistW == NULL
6923 || used_file_indexes == NULL
6924 || used_file_count == 0
6925 || used_alist_count != GARGCOUNT)
6926 return;
6927
Bram Moolenaar86b68352004-12-27 21:59:20 +00006928 /* Remember the buffer numbers for the arguments. */
6929 fnum_list = (int *)alloc((int)sizeof(int) * GARGCOUNT);
6930 if (fnum_list == NULL)
6931 return; /* out of memory */
6932 for (i = 0; i < GARGCOUNT; ++i)
6933 fnum_list[i] = GARGLIST[i].ae_fnum;
6934
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006935 /* Clear the argument list. Make room for the new arguments. */
6936 alist_clear(&global_alist);
6937 if (ga_grow(&global_alist.al_ga, used_file_count) == FAIL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00006938 return; /* out of memory */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006939
6940 for (i = 0; i < used_file_count; ++i)
6941 {
6942 idx = used_file_indexes[i];
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006943 str = utf16_to_enc(ArglistW[idx], NULL);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006944 if (str != NULL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00006945 {
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006946#ifdef FEAT_DIFF
6947 /* When using diff mode may need to concatenate file name to
6948 * directory name. Just like it's done in main(). */
6949 if (used_file_diff_mode && mch_isdir(str) && GARGCOUNT > 0
6950 && !mch_isdir(alist_name(&GARGLIST[0])))
6951 {
6952 char_u *r;
6953
6954 r = concat_fnames(str, gettail(alist_name(&GARGLIST[0])), TRUE);
6955 if (r != NULL)
6956 {
6957 vim_free(str);
6958 str = r;
6959 }
6960 }
6961#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00006962 /* Re-use the old buffer by renaming it. When not using literal
6963 * names it's done by alist_expand() below. */
6964 if (used_file_literal)
6965 buf_set_name(fnum_list[i], str);
6966
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006967 alist_add(&global_alist, str, used_file_literal ? 2 : 0);
Bram Moolenaar86b68352004-12-27 21:59:20 +00006968 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006969 }
6970
6971 if (!used_file_literal)
6972 {
6973 /* Now expand wildcards in the arguments. */
6974 /* Temporarily add '(' and ')' to 'isfname'. These are valid
6975 * filename characters but are excluded from 'isfname' to make
6976 * "gf" work on a file name in parenthesis (e.g.: see vim.h). */
6977 do_cmdline_cmd((char_u *)":let SaVe_ISF = &isf|set isf+=(,)");
Bram Moolenaar86b68352004-12-27 21:59:20 +00006978 alist_expand(fnum_list, used_alist_count);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006979 do_cmdline_cmd((char_u *)":let &isf = SaVe_ISF|unlet SaVe_ISF");
6980 }
6981
6982 /* If wildcard expansion failed, we are editing the first file of the
6983 * arglist and there is no file name: Edit the first argument now. */
6984 if (curwin->w_arg_idx == 0 && curbuf->b_fname == NULL)
6985 {
6986 do_cmdline_cmd((char_u *)":rewind");
6987 if (GARGCOUNT == 1 && used_file_full_path)
6988 (void)vim_chdirfile(alist_name(&GARGLIST[0]));
6989 }
Bram Moolenaar86b68352004-12-27 21:59:20 +00006990
6991 set_alist_count();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006992}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006993#endif
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01006994
6995 int
6996mch_setenv(char *var, char *value, int x)
6997{
6998 char_u *envbuf;
6999
7000 envbuf = alloc((unsigned)(STRLEN(var) + STRLEN(value) + 2));
7001 if (envbuf == NULL)
7002 return -1;
7003
7004 sprintf((char *)envbuf, "%s=%s", var, value);
7005
7006#ifdef FEAT_MBYTE
7007 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
7008 {
7009 WCHAR *p = enc_to_utf16(envbuf, NULL);
7010
7011 vim_free(envbuf);
7012 if (p == NULL)
7013 return -1;
7014 _wputenv(p);
7015# ifdef libintl_wputenv
7016 libintl_wputenv(p);
7017# endif
7018 /* Unlike Un*x systems, we can free the string for _wputenv(). */
7019 vim_free(p);
7020 }
7021 else
7022#endif
7023 {
7024 _putenv((char *)envbuf);
7025# ifdef libintl_putenv
7026 libintl_putenv((char *)envbuf);
7027# endif
7028 /* Unlike Un*x systems, we can free the string for _putenv(). */
7029 vim_free(envbuf);
7030 }
7031
7032 return 0;
7033}