blob: 96e115ca2f124c52d78b1536cddd343e86552b2e [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9/*
10 * os_win32.c
11 *
12 * Used for both the console version and the Win32 GUI. A lot of code is for
13 * the console version only, so there is a lot of "#ifndef FEAT_GUI_W32".
14 *
15 * Win32 (Windows NT and Windows 95) system-dependent routines.
16 * Portions lifted from the Win32 SDK samples, the MSDOS-dependent code,
17 * NetHack 3.1.3, GNU Emacs 19.30, and Vile 5.5.
18 *
19 * George V. Reilly <george@reilly.org> wrote most of this.
20 * Roger Knobbe <rogerk@wonderware.com> did the initial port of Vim 3.0.
21 */
22
Bram Moolenaar071d4272004-06-13 20:20:40 +000023#include "vim.h"
24
Bram Moolenaar325b7a22004-07-05 15:58:32 +000025#ifdef FEAT_MZSCHEME
26# include "if_mzsch.h"
27#endif
28
Bram Moolenaar071d4272004-06-13 20:20:40 +000029#include <sys/types.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000030#include <signal.h>
31#include <limits.h>
32#include <process.h>
33
34#undef chdir
35#ifdef __GNUC__
36# ifndef __MINGW32__
37# include <dirent.h>
38# endif
39#else
40# include <direct.h>
41#endif
42
43#if defined(FEAT_TITLE) && !defined(FEAT_GUI_W32)
44# include <shellapi.h>
45#endif
46
47#ifdef __MINGW32__
48# ifndef FROM_LEFT_1ST_BUTTON_PRESSED
49# define FROM_LEFT_1ST_BUTTON_PRESSED 0x0001
50# endif
51# ifndef RIGHTMOST_BUTTON_PRESSED
52# define RIGHTMOST_BUTTON_PRESSED 0x0002
53# endif
54# ifndef FROM_LEFT_2ND_BUTTON_PRESSED
55# define FROM_LEFT_2ND_BUTTON_PRESSED 0x0004
56# endif
57# ifndef FROM_LEFT_3RD_BUTTON_PRESSED
58# define FROM_LEFT_3RD_BUTTON_PRESSED 0x0008
59# endif
60# ifndef FROM_LEFT_4TH_BUTTON_PRESSED
61# define FROM_LEFT_4TH_BUTTON_PRESSED 0x0010
62# endif
63
64/*
65 * EventFlags
66 */
67# ifndef MOUSE_MOVED
68# define MOUSE_MOVED 0x0001
69# endif
70# ifndef DOUBLE_CLICK
71# define DOUBLE_CLICK 0x0002
72# endif
73#endif
74
75/* Record all output and all keyboard & mouse input */
76/* #define MCH_WRITE_DUMP */
77
78#ifdef MCH_WRITE_DUMP
79FILE* fdDump = NULL;
80#endif
81
82/*
83 * When generating prototypes for Win32 on Unix, these lines make the syntax
84 * errors disappear. They do not need to be correct.
85 */
86#ifdef PROTO
87#define WINAPI
88#define WINBASEAPI
89typedef char * LPCSTR;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +000090typedef char * LPWSTR;
Bram Moolenaar071d4272004-06-13 20:20:40 +000091typedef int ACCESS_MASK;
92typedef int BOOL;
93typedef int COLORREF;
94typedef int CONSOLE_CURSOR_INFO;
95typedef int COORD;
96typedef int DWORD;
97typedef int HANDLE;
98typedef int HDC;
99typedef int HFONT;
100typedef int HICON;
101typedef int HINSTANCE;
102typedef int HWND;
103typedef int INPUT_RECORD;
104typedef int KEY_EVENT_RECORD;
105typedef int LOGFONT;
106typedef int LPBOOL;
107typedef int LPCTSTR;
108typedef int LPDWORD;
109typedef int LPSTR;
110typedef int LPTSTR;
111typedef int LPVOID;
112typedef int MOUSE_EVENT_RECORD;
113typedef int PACL;
114typedef int PDWORD;
115typedef int PHANDLE;
116typedef int PRINTDLG;
117typedef int PSECURITY_DESCRIPTOR;
118typedef int PSID;
119typedef int SECURITY_INFORMATION;
120typedef int SHORT;
121typedef int SMALL_RECT;
122typedef int TEXTMETRIC;
123typedef int TOKEN_INFORMATION_CLASS;
124typedef int TRUSTEE;
125typedef int WORD;
126typedef int WCHAR;
127typedef void VOID;
128#endif
129
130#ifndef FEAT_GUI_W32
131/* Undocumented API in kernel32.dll needed to work around dead key bug in
132 * console-mode applications in NT 4.0. If you switch keyboard layouts
133 * in a console app to a layout that includes dead keys and then hit a
134 * dead key, a call to ToAscii will trash the stack. My thanks to Ian James
135 * and Michael Dietrich for helping me figure out this workaround.
136 */
137
138/* WINBASEAPI BOOL WINAPI GetConsoleKeyboardLayoutNameA(LPSTR); */
139#ifndef WINBASEAPI
140# define WINBASEAPI __stdcall
141#endif
142#if defined(__BORLANDC__)
143typedef BOOL (__stdcall *PFNGCKLN)(LPSTR);
144#else
145typedef WINBASEAPI BOOL (WINAPI *PFNGCKLN)(LPSTR);
146#endif
Bram Moolenaard6f676d2005-06-01 21:51:55 +0000147static PFNGCKLN s_pfnGetConsoleKeyboardLayoutName = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000148#endif
149
150#if defined(__BORLANDC__)
151/* Strangely Borland uses a non-standard name. */
152# define wcsicmp(a, b) wcscmpi((a), (b))
153#endif
154
Bram Moolenaar8c85fa32011-08-10 17:08:03 +0200155/* Enable common dialogs input unicode from IME if posible. */
156#ifdef FEAT_MBYTE
157LRESULT (WINAPI *pDispatchMessage)(LPMSG) = DispatchMessage;
158BOOL (WINAPI *pGetMessage)(LPMSG, HWND, UINT, UINT) = GetMessage;
159BOOL (WINAPI *pIsDialogMessage)(HWND, LPMSG) = IsDialogMessage;
160BOOL (WINAPI *pPeekMessage)(LPMSG, HWND, UINT, UINT, UINT) = PeekMessage;
161#endif
162
Bram Moolenaar071d4272004-06-13 20:20:40 +0000163#ifndef FEAT_GUI_W32
164/* Win32 Console handles for input and output */
165static HANDLE g_hConIn = INVALID_HANDLE_VALUE;
166static HANDLE g_hConOut = INVALID_HANDLE_VALUE;
167
168/* Win32 Screen buffer,coordinate,console I/O information */
169static SMALL_RECT g_srScrollRegion;
170static COORD g_coord; /* 0-based, but external coords are 1-based */
171
172/* The attribute of the screen when the editor was started */
173static WORD g_attrDefault = 7; /* lightgray text on black background */
174static WORD g_attrCurrent;
175
176static int g_fCBrkPressed = FALSE; /* set by ctrl-break interrupt */
177static int g_fCtrlCPressed = FALSE; /* set when ctrl-C or ctrl-break detected */
178static int g_fForceExit = FALSE; /* set when forcefully exiting */
179
180static void termcap_mode_start(void);
181static void termcap_mode_end(void);
182static void clear_chars(COORD coord, DWORD n);
183static void clear_screen(void);
184static void clear_to_end_of_display(void);
185static void clear_to_end_of_line(void);
186static void scroll(unsigned cLines);
187static void set_scroll_region(unsigned left, unsigned top,
188 unsigned right, unsigned bottom);
189static void insert_lines(unsigned cLines);
190static void delete_lines(unsigned cLines);
191static void gotoxy(unsigned x, unsigned y);
192static void normvideo(void);
193static void textattr(WORD wAttr);
194static void textcolor(WORD wAttr);
195static void textbackground(WORD wAttr);
196static void standout(void);
197static void standend(void);
198static void visual_bell(void);
199static void cursor_visible(BOOL fVisible);
200static BOOL write_chars(LPCSTR pchBuf, DWORD cchToWrite);
201static char_u tgetch(int *pmodifiers, char_u *pch2);
202static void create_conin(void);
203static int s_cursor_visible = TRUE;
204static int did_create_conin = FALSE;
205#else
206static int s_dont_use_vimrun = TRUE;
207static int need_vimrun_warning = FALSE;
208static char *vimrun_path = "vimrun ";
209#endif
210
211#ifndef FEAT_GUI_W32
212static int suppress_winsize = 1; /* don't fiddle with console */
213#endif
214
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200215static char_u *exe_path = NULL;
216
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217 static void
218get_exe_name(void)
219{
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100220 /* Maximum length of $PATH is more than MAXPATHL. 8191 is often mentioned
221 * as the maximum length that works (plus a NUL byte). */
222#define MAX_ENV_PATH_LEN 8192
223 char temp[MAX_ENV_PATH_LEN];
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200224 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000225
226 if (exe_name == NULL)
227 {
228 /* store the name of the executable, may be used for $VIM */
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100229 GetModuleFileName(NULL, temp, MAX_ENV_PATH_LEN - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000230 if (*temp != NUL)
231 exe_name = FullName_save((char_u *)temp, FALSE);
232 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000233
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200234 if (exe_path == NULL && exe_name != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000235 {
Bram Moolenaar6b5ef062010-10-27 12:18:00 +0200236 exe_path = vim_strnsave(exe_name,
237 (int)(gettail_sep(exe_name) - exe_name));
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200238 if (exe_path != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000239 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200240 /* Append our starting directory to $PATH, so that when doing
241 * "!xxd" it's found in our starting directory. Needed because
242 * SearchPath() also looks there. */
243 p = mch_getenv("PATH");
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100244 if (p == NULL
245 || STRLEN(p) + STRLEN(exe_path) + 2 < MAX_ENV_PATH_LEN)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200246 {
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100247 if (p == NULL || *p == NUL)
248 temp[0] = NUL;
249 else
250 {
251 STRCPY(temp, p);
252 STRCAT(temp, ";");
253 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200254 STRCAT(temp, exe_path);
255 vim_setenv((char_u *)"PATH", temp);
256 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000257 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000258 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000259}
260
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200261/*
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100262 * Unescape characters in "p" that appear in "escaped".
263 */
264 static void
265unescape_shellxquote(char_u *p, char_u *escaped)
266{
Bram Moolenaar4336cdf2012-02-29 13:58:47 +0100267 int l = (int)STRLEN(p);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100268 int n;
269
270 while (*p != NUL)
271 {
272 if (*p == '^' && vim_strchr(escaped, p[1]) != NULL)
273 mch_memmove(p, p + 1, l--);
274#ifdef FEAT_MBYTE
275 n = (*mb_ptr2len)(p);
276#else
277 n = 1;
278#endif
279 p += n;
280 l -= n;
281 }
282}
283
284/*
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200285 * Load library "name".
286 */
287 HINSTANCE
288vimLoadLib(char *name)
289{
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200290 HINSTANCE dll = NULL;
291 char old_dir[MAXPATHL];
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200292
Bram Moolenaarfaca8402012-10-21 02:37:10 +0200293 /* NOTE: Do not use mch_dirname() and mch_chdir() here, they may call
294 * vimLoadLib() recursively, which causes a stack overflow. */
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200295 if (exe_path == NULL)
296 get_exe_name();
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200297 if (exe_path != NULL)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200298 {
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200299#ifdef FEAT_MBYTE
300 WCHAR old_dirw[MAXPATHL];
301
302 if (GetCurrentDirectoryW(MAXPATHL, old_dirw) != 0)
303 {
304 /* Change directory to where the executable is, both to make
305 * sure we find a .dll there and to avoid looking for a .dll
306 * in the current directory. */
307 SetCurrentDirectory(exe_path);
308 dll = LoadLibrary(name);
309 SetCurrentDirectoryW(old_dirw);
310 return dll;
311 }
312 /* Retry with non-wide function (for Windows 98). */
313 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
314#endif
315 if (GetCurrentDirectory(MAXPATHL, old_dir) != 0)
316 {
317 /* Change directory to where the executable is, both to make
318 * sure we find a .dll there and to avoid looking for a .dll
319 * in the current directory. */
320 SetCurrentDirectory(exe_path);
321 dll = LoadLibrary(name);
322 SetCurrentDirectory(old_dir);
323 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200324 }
325 return dll;
326}
327
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328#if defined(DYNAMIC_GETTEXT) || defined(PROTO)
329# ifndef GETTEXT_DLL
330# define GETTEXT_DLL "libintl.dll"
331# endif
Bram Moolenaarf6a2b082012-06-29 13:14:03 +0200332/* Dummy functions */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000333static char *null_libintl_gettext(const char *);
334static char *null_libintl_textdomain(const char *);
335static char *null_libintl_bindtextdomain(const char *, const char *);
336static char *null_libintl_bind_textdomain_codeset(const char *, const char *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000337
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200338static HINSTANCE hLibintlDLL = NULL;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000339char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
340char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
341char *(*dyn_libintl_bindtextdomain)(const char *, const char *)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000342 = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000343char *(*dyn_libintl_bind_textdomain_codeset)(const char *, const char *)
344 = null_libintl_bind_textdomain_codeset;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000345
346 int
347dyn_libintl_init(char *libname)
348{
349 int i;
350 static struct
351 {
352 char *name;
353 FARPROC *ptr;
354 } libintl_entry[] =
355 {
356 {"gettext", (FARPROC*)&dyn_libintl_gettext},
357 {"textdomain", (FARPROC*)&dyn_libintl_textdomain},
358 {"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
359 {NULL, NULL}
360 };
361
362 /* No need to initialize twice. */
363 if (hLibintlDLL)
364 return 1;
365 /* Load gettext library (libintl.dll) */
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200366 hLibintlDLL = vimLoadLib(libname != NULL ? libname : GETTEXT_DLL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000367 if (!hLibintlDLL)
368 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200369 if (p_verbose > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000370 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200371 verbose_enter();
372 EMSG2(_(e_loadlib), GETTEXT_DLL);
373 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000374 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200375 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000376 }
377 for (i = 0; libintl_entry[i].name != NULL
378 && libintl_entry[i].ptr != NULL; ++i)
379 {
380 if ((*libintl_entry[i].ptr = (FARPROC)GetProcAddress(hLibintlDLL,
381 libintl_entry[i].name)) == NULL)
382 {
383 dyn_libintl_end();
384 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000385 {
386 verbose_enter();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387 EMSG2(_(e_loadfunc), libintl_entry[i].name);
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000388 verbose_leave();
389 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000390 return 0;
391 }
392 }
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000393
394 /* The bind_textdomain_codeset() function is optional. */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000395 dyn_libintl_bind_textdomain_codeset = (void *)GetProcAddress(hLibintlDLL,
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000396 "bind_textdomain_codeset");
397 if (dyn_libintl_bind_textdomain_codeset == NULL)
398 dyn_libintl_bind_textdomain_codeset =
399 null_libintl_bind_textdomain_codeset;
400
Bram Moolenaar071d4272004-06-13 20:20:40 +0000401 return 1;
402}
403
404 void
405dyn_libintl_end()
406{
407 if (hLibintlDLL)
408 FreeLibrary(hLibintlDLL);
409 hLibintlDLL = NULL;
410 dyn_libintl_gettext = null_libintl_gettext;
411 dyn_libintl_textdomain = null_libintl_textdomain;
412 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000413 dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000414}
415
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000416/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000417 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000418null_libintl_gettext(const char *msgid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000419{
420 return (char*)msgid;
421}
422
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000423/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000424 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000425null_libintl_bindtextdomain(const char *domainname, const char *dirname)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426{
427 return NULL;
428}
429
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000430/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000431 static char *
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000432null_libintl_bind_textdomain_codeset(const char *domainname,
433 const char *codeset)
434{
435 return NULL;
436}
437
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000438/*ARGSUSED*/
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000439 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000440null_libintl_textdomain(const char *domainname)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000441{
442 return NULL;
443}
444
445#endif /* DYNAMIC_GETTEXT */
446
447/* This symbol is not defined in older versions of the SDK or Visual C++ */
448
449#ifndef VER_PLATFORM_WIN32_WINDOWS
450# define VER_PLATFORM_WIN32_WINDOWS 1
451#endif
452
453DWORD g_PlatformId;
454
455#ifdef HAVE_ACL
456# include <aclapi.h>
457/*
458 * These are needed to dynamically load the ADVAPI DLL, which is not
459 * implemented under Windows 95 (and causes VIM to crash)
460 */
461typedef DWORD (WINAPI *PSNSECINFO) (LPTSTR, enum SE_OBJECT_TYPE,
462 SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
463typedef DWORD (WINAPI *PGNSECINFO) (LPSTR, enum SE_OBJECT_TYPE,
464 SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *,
465 PSECURITY_DESCRIPTOR *);
466
467static HANDLE advapi_lib = NULL; /* Handle for ADVAPI library */
468static PSNSECINFO pSetNamedSecurityInfo;
469static PGNSECINFO pGetNamedSecurityInfo;
470#endif
471
Bram Moolenaar4b9669f2011-07-07 16:20:52 +0200472typedef BOOL (WINAPI *PSETHANDLEINFORMATION)(HANDLE, DWORD, DWORD);
473
474static BOOL allowPiping = FALSE;
475static PSETHANDLEINFORMATION pSetHandleInformation;
476
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477/*
478 * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
479 * VER_PLATFORM_WIN32_WINDOWS (Win95).
480 */
481 void
482PlatformId(void)
483{
484 static int done = FALSE;
485
486 if (!done)
487 {
488 OSVERSIONINFO ovi;
489
490 ovi.dwOSVersionInfoSize = sizeof(ovi);
491 GetVersionEx(&ovi);
492
493 g_PlatformId = ovi.dwPlatformId;
494
495#ifdef HAVE_ACL
496 /*
497 * Load the ADVAPI runtime if we are on anything
498 * other than Windows 95
499 */
500 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
501 {
502 /*
503 * do this load. Problems: Doesn't unload at end of run (this is
504 * theoretically okay, since Windows should unload it when VIM
505 * terminates). Should we be using the 'mch_libcall' routines?
506 * Seems like a lot of overhead to load/unload ADVAPI32.DLL each
507 * time we verify security...
508 */
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200509 advapi_lib = vimLoadLib("ADVAPI32.DLL");
Bram Moolenaar071d4272004-06-13 20:20:40 +0000510 if (advapi_lib != NULL)
511 {
512 pSetNamedSecurityInfo = (PSNSECINFO)GetProcAddress(advapi_lib,
513 "SetNamedSecurityInfoA");
514 pGetNamedSecurityInfo = (PGNSECINFO)GetProcAddress(advapi_lib,
515 "GetNamedSecurityInfoA");
516 if (pSetNamedSecurityInfo == NULL
517 || pGetNamedSecurityInfo == NULL)
518 {
519 /* If we can't get the function addresses, set advapi_lib
520 * to NULL so that we don't use them. */
521 FreeLibrary(advapi_lib);
522 advapi_lib = NULL;
523 }
524 }
525 }
526#endif
Bram Moolenaar4b9669f2011-07-07 16:20:52 +0200527 /*
528 * If we are on windows NT, try to load the pipe functions, only
529 * available from Win2K.
530 */
531 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
532 {
533 HANDLE kernel32 = GetModuleHandle("kernel32");
534 pSetHandleInformation = (PSETHANDLEINFORMATION)GetProcAddress(
535 kernel32, "SetHandleInformation");
536
537 allowPiping = pSetHandleInformation != NULL;
538 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000539 done = TRUE;
540 }
541}
542
543/*
544 * Return TRUE when running on Windows 95 (or 98 or ME).
545 * Only to be used after mch_init().
546 */
547 int
548mch_windows95(void)
549{
550 return g_PlatformId == VER_PLATFORM_WIN32_WINDOWS;
551}
552
553#ifdef FEAT_GUI_W32
554/*
555 * Used to work around the "can't do synchronous spawn"
556 * problem on Win32s, without resorting to Universal Thunk.
557 */
558static int old_num_windows;
559static int num_windows;
560
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000561/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000562 static BOOL CALLBACK
563win32ssynch_cb(HWND hwnd, LPARAM lparam)
564{
565 num_windows++;
566 return TRUE;
567}
568#endif
569
570#ifndef FEAT_GUI_W32
571
572#define SHIFT (SHIFT_PRESSED)
573#define CTRL (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
574#define ALT (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
575#define ALT_GR (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)
576
577
578/* When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
579 * We map function keys to their ANSI terminal equivalents, as produced
580 * by ANSI.SYS, for compatibility with the MS-DOS version of Vim. Any
581 * ANSI key with a value >= '\300' is nonstandard, but provided anyway
582 * so that the user can have access to all SHIFT-, CTRL-, and ALT-
583 * combinations of function/arrow/etc keys.
584 */
585
Bram Moolenaard6f676d2005-06-01 21:51:55 +0000586static const struct
Bram Moolenaar071d4272004-06-13 20:20:40 +0000587{
588 WORD wVirtKey;
589 BOOL fAnsiKey;
590 int chAlone;
591 int chShift;
592 int chCtrl;
593 int chAlt;
594} VirtKeyMap[] =
595{
596
597/* Key ANSI alone shift ctrl alt */
598 { VK_ESCAPE,FALSE, ESC, ESC, ESC, ESC, },
599
600 { VK_F1, TRUE, ';', 'T', '^', 'h', },
601 { VK_F2, TRUE, '<', 'U', '_', 'i', },
602 { VK_F3, TRUE, '=', 'V', '`', 'j', },
603 { VK_F4, TRUE, '>', 'W', 'a', 'k', },
604 { VK_F5, TRUE, '?', 'X', 'b', 'l', },
605 { VK_F6, TRUE, '@', 'Y', 'c', 'm', },
606 { VK_F7, TRUE, 'A', 'Z', 'd', 'n', },
607 { VK_F8, TRUE, 'B', '[', 'e', 'o', },
608 { VK_F9, TRUE, 'C', '\\', 'f', 'p', },
609 { VK_F10, TRUE, 'D', ']', 'g', 'q', },
610 { VK_F11, TRUE, '\205', '\207', '\211', '\213', },
611 { VK_F12, TRUE, '\206', '\210', '\212', '\214', },
612
613 { VK_HOME, TRUE, 'G', '\302', 'w', '\303', },
614 { VK_UP, TRUE, 'H', '\304', '\305', '\306', },
615 { VK_PRIOR, TRUE, 'I', '\307', '\204', '\310', }, /*PgUp*/
616 { VK_LEFT, TRUE, 'K', '\311', 's', '\312', },
617 { VK_RIGHT, TRUE, 'M', '\313', 't', '\314', },
618 { VK_END, TRUE, 'O', '\315', 'u', '\316', },
619 { VK_DOWN, TRUE, 'P', '\317', '\320', '\321', },
620 { VK_NEXT, TRUE, 'Q', '\322', 'v', '\323', }, /*PgDn*/
621 { VK_INSERT,TRUE, 'R', '\324', '\325', '\326', },
622 { VK_DELETE,TRUE, 'S', '\327', '\330', '\331', },
623
624 { VK_SNAPSHOT,TRUE, 0, 0, 0, 'r', }, /*PrtScrn*/
625
626#if 0
627 /* Most people don't have F13-F20, but what the hell... */
628 { VK_F13, TRUE, '\332', '\333', '\334', '\335', },
629 { VK_F14, TRUE, '\336', '\337', '\340', '\341', },
630 { VK_F15, TRUE, '\342', '\343', '\344', '\345', },
631 { VK_F16, TRUE, '\346', '\347', '\350', '\351', },
632 { VK_F17, TRUE, '\352', '\353', '\354', '\355', },
633 { VK_F18, TRUE, '\356', '\357', '\360', '\361', },
634 { VK_F19, TRUE, '\362', '\363', '\364', '\365', },
635 { VK_F20, TRUE, '\366', '\367', '\370', '\371', },
636#endif
637 { VK_ADD, TRUE, 'N', 'N', 'N', 'N', }, /* keyp '+' */
638 { VK_SUBTRACT, TRUE,'J', 'J', 'J', 'J', }, /* keyp '-' */
639 /* { VK_DIVIDE, TRUE,'N', 'N', 'N', 'N', }, keyp '/' */
640 { VK_MULTIPLY, TRUE,'7', '7', '7', '7', }, /* keyp '*' */
641
642 { VK_NUMPAD0,TRUE, '\332', '\333', '\334', '\335', },
643 { VK_NUMPAD1,TRUE, '\336', '\337', '\340', '\341', },
644 { VK_NUMPAD2,TRUE, '\342', '\343', '\344', '\345', },
645 { VK_NUMPAD3,TRUE, '\346', '\347', '\350', '\351', },
646 { VK_NUMPAD4,TRUE, '\352', '\353', '\354', '\355', },
647 { VK_NUMPAD5,TRUE, '\356', '\357', '\360', '\361', },
648 { VK_NUMPAD6,TRUE, '\362', '\363', '\364', '\365', },
649 { VK_NUMPAD7,TRUE, '\366', '\367', '\370', '\371', },
650 { VK_NUMPAD8,TRUE, '\372', '\373', '\374', '\375', },
651 /* Sorry, out of number space! <negri>*/
652 { VK_NUMPAD9,TRUE, '\376', '\377', '\377', '\367', },
653
654};
655
656
657#ifdef _MSC_VER
658// The ToAscii bug destroys several registers. Need to turn off optimization
659// or the GetConsoleKeyboardLayoutName hack will fail in non-debug versions
Bram Moolenaar7b5f8322006-03-23 22:47:08 +0000660# pragma warning(push)
661# pragma warning(disable: 4748)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000662# pragma optimize("", off)
663#endif
664
665#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__)
666# define AChar AsciiChar
667#else
668# define AChar uChar.AsciiChar
669#endif
670
671/* The return code indicates key code size. */
672 static int
673#ifdef __BORLANDC__
674 __stdcall
675#endif
676win32_kbd_patch_key(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000677 KEY_EVENT_RECORD *pker)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000678{
679 UINT uMods = pker->dwControlKeyState;
680 static int s_iIsDead = 0;
681 static WORD awAnsiCode[2];
682 static BYTE abKeystate[256];
683
684
685 if (s_iIsDead == 2)
686 {
687 pker->AChar = (CHAR) awAnsiCode[1];
688 s_iIsDead = 0;
689 return 1;
690 }
691
692 if (pker->AChar != 0)
693 return 1;
694
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200695 vim_memset(abKeystate, 0, sizeof (abKeystate));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000696
697 // Should only be non-NULL on NT 4.0
698 if (s_pfnGetConsoleKeyboardLayoutName != NULL)
699 {
700 CHAR szKLID[KL_NAMELENGTH];
701
702 if ((*s_pfnGetConsoleKeyboardLayoutName)(szKLID))
703 (void)LoadKeyboardLayout(szKLID, KLF_ACTIVATE);
704 }
705
706 /* Clear any pending dead keys */
707 ToAscii(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 0);
708
709 if (uMods & SHIFT_PRESSED)
710 abKeystate[VK_SHIFT] = 0x80;
711 if (uMods & CAPSLOCK_ON)
712 abKeystate[VK_CAPITAL] = 1;
713
714 if ((uMods & ALT_GR) == ALT_GR)
715 {
716 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
717 abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
718 }
719
720 s_iIsDead = ToAscii(pker->wVirtualKeyCode, pker->wVirtualScanCode,
721 abKeystate, awAnsiCode, 0);
722
723 if (s_iIsDead > 0)
724 pker->AChar = (CHAR) awAnsiCode[0];
725
726 return s_iIsDead;
727}
728
729#ifdef _MSC_VER
730/* MUST switch optimization on again here, otherwise a call to
731 * decode_key_event() may crash (e.g. when hitting caps-lock) */
732# pragma optimize("", on)
Bram Moolenaar7b5f8322006-03-23 22:47:08 +0000733# pragma warning(pop)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000734
735# if (_MSC_VER < 1100)
736/* MUST turn off global optimisation for this next function, or
737 * pressing ctrl-minus in insert mode crashes Vim when built with
738 * VC4.1. -- negri. */
739# pragma optimize("g", off)
740# endif
741#endif
742
743static BOOL g_fJustGotFocus = FALSE;
744
745/*
746 * Decode a KEY_EVENT into one or two keystrokes
747 */
748 static BOOL
749decode_key_event(
750 KEY_EVENT_RECORD *pker,
751 char_u *pch,
752 char_u *pch2,
753 int *pmodifiers,
754 BOOL fDoPost)
755{
756 int i;
757 const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
758
759 *pch = *pch2 = NUL;
760 g_fJustGotFocus = FALSE;
761
762 /* ignore key up events */
763 if (!pker->bKeyDown)
764 return FALSE;
765
766 /* ignore some keystrokes */
767 switch (pker->wVirtualKeyCode)
768 {
769 /* modifiers */
770 case VK_SHIFT:
771 case VK_CONTROL:
772 case VK_MENU: /* Alt key */
773 return FALSE;
774
775 default:
776 break;
777 }
778
779 /* special cases */
780 if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->AChar == NUL)
781 {
782 /* Ctrl-6 is Ctrl-^ */
783 if (pker->wVirtualKeyCode == '6')
784 {
785 *pch = Ctrl_HAT;
786 return TRUE;
787 }
788 /* Ctrl-2 is Ctrl-@ */
789 else if (pker->wVirtualKeyCode == '2')
790 {
791 *pch = NUL;
792 return TRUE;
793 }
794 /* Ctrl-- is Ctrl-_ */
795 else if (pker->wVirtualKeyCode == 0xBD)
796 {
797 *pch = Ctrl__;
798 return TRUE;
799 }
800 }
801
802 /* Shift-TAB */
803 if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED))
804 {
805 *pch = K_NUL;
806 *pch2 = '\017';
807 return TRUE;
808 }
809
810 for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]); --i >= 0; )
811 {
812 if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
813 {
814 if (nModifs == 0)
815 *pch = VirtKeyMap[i].chAlone;
816 else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
817 *pch = VirtKeyMap[i].chShift;
818 else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
819 *pch = VirtKeyMap[i].chCtrl;
820 else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
821 *pch = VirtKeyMap[i].chAlt;
822
823 if (*pch != 0)
824 {
825 if (VirtKeyMap[i].fAnsiKey)
826 {
827 *pch2 = *pch;
828 *pch = K_NUL;
829 }
830
831 return TRUE;
832 }
833 }
834 }
835
836 i = win32_kbd_patch_key(pker);
837
838 if (i < 0)
839 *pch = NUL;
840 else
841 {
842 *pch = (i > 0) ? pker->AChar : NUL;
843
844 if (pmodifiers != NULL)
845 {
846 /* Pass on the ALT key as a modifier, but only when not combined
847 * with CTRL (which is ALTGR). */
848 if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0)
849 *pmodifiers |= MOD_MASK_ALT;
850
851 /* Pass on SHIFT only for special keys, because we don't know when
852 * it's already included with the character. */
853 if ((nModifs & SHIFT) != 0 && *pch <= 0x20)
854 *pmodifiers |= MOD_MASK_SHIFT;
855
856 /* Pass on CTRL only for non-special keys, because we don't know
857 * when it's already included with the character. And not when
858 * combined with ALT (which is ALTGR). */
859 if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0
860 && *pch >= 0x20 && *pch < 0x80)
861 *pmodifiers |= MOD_MASK_CTRL;
862 }
863 }
864
865 return (*pch != NUL);
866}
867
868#ifdef _MSC_VER
869# pragma optimize("", on)
870#endif
871
872#endif /* FEAT_GUI_W32 */
873
874
875#ifdef FEAT_MOUSE
876
877/*
878 * For the GUI the mouse handling is in gui_w32.c.
879 */
880# ifdef FEAT_GUI_W32
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000881/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000882 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000883mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000884{
885}
886# else
887static int g_fMouseAvail = FALSE; /* mouse present */
888static int g_fMouseActive = FALSE; /* mouse enabled */
889static int g_nMouseClick = -1; /* mouse status */
890static int g_xMouse; /* mouse x coordinate */
891static int g_yMouse; /* mouse y coordinate */
892
893/*
894 * Enable or disable mouse input
895 */
896 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000897mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000898{
899 DWORD cmodein;
900
901 if (!g_fMouseAvail)
902 return;
903
904 g_fMouseActive = on;
905 GetConsoleMode(g_hConIn, &cmodein);
906
907 if (g_fMouseActive)
908 cmodein |= ENABLE_MOUSE_INPUT;
909 else
910 cmodein &= ~ENABLE_MOUSE_INPUT;
911
912 SetConsoleMode(g_hConIn, cmodein);
913}
914
915
916/*
917 * Decode a MOUSE_EVENT. If it's a valid event, return MOUSE_LEFT,
918 * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
919 * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
920 * or a MOUSE_LEFT, _MIDDLE, or _RIGHT. We encode the button type,
921 * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
922 * and we return the mouse position in g_xMouse and g_yMouse.
923 *
924 * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
925 * MOUSE_DRAGs and one MOUSE_RELEASE. MOUSE_RELEASE will be followed only
926 * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
927 *
928 * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
929 * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
930 *
931 * Windows will send us MOUSE_MOVED notifications whenever the mouse
932 * moves, even if it stays within the same character cell. We ignore
933 * all MOUSE_MOVED messages if the position hasn't really changed, and
934 * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
935 * we're only interested in MOUSE_DRAG).
936 *
937 * All of this is complicated by the code that fakes MOUSE_MIDDLE on
938 * 2-button mouses by pressing the left & right buttons simultaneously.
939 * In practice, it's almost impossible to click both at the same time,
940 * so we need to delay a little. Also, we tend not to get MOUSE_RELEASE
941 * in such cases, if the user is clicking quickly.
942 */
943 static BOOL
944decode_mouse_event(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000945 MOUSE_EVENT_RECORD *pmer)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946{
947 static int s_nOldButton = -1;
948 static int s_nOldMouseClick = -1;
949 static int s_xOldMouse = -1;
950 static int s_yOldMouse = -1;
951 static linenr_T s_old_topline = 0;
952#ifdef FEAT_DIFF
953 static int s_old_topfill = 0;
954#endif
955 static int s_cClicks = 1;
956 static BOOL s_fReleased = TRUE;
957 static DWORD s_dwLastClickTime = 0;
958 static BOOL s_fNextIsMiddle = FALSE;
959
960 static DWORD cButtons = 0; /* number of buttons supported */
961
962 const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
963 const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
964 const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
965 const DWORD LEFT_RIGHT = LEFT | RIGHT;
966
967 int nButton;
968
969 if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons))
970 cButtons = 2;
971
972 if (!g_fMouseAvail || !g_fMouseActive)
973 {
974 g_nMouseClick = -1;
975 return FALSE;
976 }
977
978 /* get a spurious MOUSE_EVENT immediately after receiving focus; ignore */
979 if (g_fJustGotFocus)
980 {
981 g_fJustGotFocus = FALSE;
982 return FALSE;
983 }
984
985 /* unprocessed mouse click? */
986 if (g_nMouseClick != -1)
987 return TRUE;
988
989 nButton = -1;
990 g_xMouse = pmer->dwMousePosition.X;
991 g_yMouse = pmer->dwMousePosition.Y;
992
993 if (pmer->dwEventFlags == MOUSE_MOVED)
994 {
995 /* ignore MOUSE_MOVED events if (x, y) hasn't changed. (We get these
996 * events even when the mouse moves only within a char cell.) */
997 if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse)
998 return FALSE;
999 }
1000
1001 /* If no buttons are pressed... */
1002 if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0)
1003 {
1004 /* If the last thing returned was MOUSE_RELEASE, ignore this */
1005 if (s_fReleased)
1006 return FALSE;
1007
1008 nButton = MOUSE_RELEASE;
1009 s_fReleased = TRUE;
1010 }
1011 else /* one or more buttons pressed */
1012 {
1013 /* on a 2-button mouse, hold down left and right buttons
1014 * simultaneously to get MIDDLE. */
1015
1016 if (cButtons == 2 && s_nOldButton != MOUSE_DRAG)
1017 {
1018 DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
1019
1020 /* if either left or right button only is pressed, see if the
1021 * the next mouse event has both of them pressed */
1022 if (dwLR == LEFT || dwLR == RIGHT)
1023 {
1024 for (;;)
1025 {
1026 /* wait a short time for next input event */
1027 if (WaitForSingleObject(g_hConIn, p_mouset / 3)
1028 != WAIT_OBJECT_0)
1029 break;
1030 else
1031 {
1032 DWORD cRecords = 0;
1033 INPUT_RECORD ir;
1034 MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
1035
1036 PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
1037
1038 if (cRecords == 0 || ir.EventType != MOUSE_EVENT
1039 || !(pmer2->dwButtonState & LEFT_RIGHT))
1040 break;
1041 else
1042 {
1043 if (pmer2->dwEventFlags != MOUSE_MOVED)
1044 {
1045 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
1046
1047 return decode_mouse_event(pmer2);
1048 }
1049 else if (s_xOldMouse == pmer2->dwMousePosition.X &&
1050 s_yOldMouse == pmer2->dwMousePosition.Y)
1051 {
1052 /* throw away spurious mouse move */
1053 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
1054
1055 /* are there any more mouse events in queue? */
1056 PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
1057
1058 if (cRecords==0 || ir.EventType != MOUSE_EVENT)
1059 break;
1060 }
1061 else
1062 break;
1063 }
1064 }
1065 }
1066 }
1067 }
1068
1069 if (s_fNextIsMiddle)
1070 {
1071 nButton = (pmer->dwEventFlags == MOUSE_MOVED)
1072 ? MOUSE_DRAG : MOUSE_MIDDLE;
1073 s_fNextIsMiddle = FALSE;
1074 }
1075 else if (cButtons == 2 &&
1076 ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
1077 {
1078 nButton = MOUSE_MIDDLE;
1079
1080 if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED)
1081 {
1082 s_fNextIsMiddle = TRUE;
1083 nButton = MOUSE_RELEASE;
1084 }
1085 }
1086 else if ((pmer->dwButtonState & LEFT) == LEFT)
1087 nButton = MOUSE_LEFT;
1088 else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
1089 nButton = MOUSE_MIDDLE;
1090 else if ((pmer->dwButtonState & RIGHT) == RIGHT)
1091 nButton = MOUSE_RIGHT;
1092
1093 if (! s_fReleased && ! s_fNextIsMiddle
1094 && nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG)
1095 return FALSE;
1096
1097 s_fReleased = s_fNextIsMiddle;
1098 }
1099
1100 if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK)
1101 {
1102 /* button pressed or released, without mouse moving */
1103 if (nButton != -1 && nButton != MOUSE_RELEASE)
1104 {
1105 DWORD dwCurrentTime = GetTickCount();
1106
1107 if (s_xOldMouse != g_xMouse
1108 || s_yOldMouse != g_yMouse
1109 || s_nOldButton != nButton
1110 || s_old_topline != curwin->w_topline
1111#ifdef FEAT_DIFF
1112 || s_old_topfill != curwin->w_topfill
1113#endif
1114 || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset)
1115 {
1116 s_cClicks = 1;
1117 }
1118 else if (++s_cClicks > 4)
1119 {
1120 s_cClicks = 1;
1121 }
1122
1123 s_dwLastClickTime = dwCurrentTime;
1124 }
1125 }
1126 else if (pmer->dwEventFlags == MOUSE_MOVED)
1127 {
1128 if (nButton != -1 && nButton != MOUSE_RELEASE)
1129 nButton = MOUSE_DRAG;
1130
1131 s_cClicks = 1;
1132 }
1133
1134 if (nButton == -1)
1135 return FALSE;
1136
1137 if (nButton != MOUSE_RELEASE)
1138 s_nOldButton = nButton;
1139
1140 g_nMouseClick = nButton;
1141
1142 if (pmer->dwControlKeyState & SHIFT_PRESSED)
1143 g_nMouseClick |= MOUSE_SHIFT;
1144 if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
1145 g_nMouseClick |= MOUSE_CTRL;
1146 if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
1147 g_nMouseClick |= MOUSE_ALT;
1148
1149 if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE)
1150 SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
1151
1152 /* only pass on interesting (i.e., different) mouse events */
1153 if (s_xOldMouse == g_xMouse
1154 && s_yOldMouse == g_yMouse
1155 && s_nOldMouseClick == g_nMouseClick)
1156 {
1157 g_nMouseClick = -1;
1158 return FALSE;
1159 }
1160
1161 s_xOldMouse = g_xMouse;
1162 s_yOldMouse = g_yMouse;
1163 s_old_topline = curwin->w_topline;
1164#ifdef FEAT_DIFF
1165 s_old_topfill = curwin->w_topfill;
1166#endif
1167 s_nOldMouseClick = g_nMouseClick;
1168
1169 return TRUE;
1170}
1171
1172# endif /* FEAT_GUI_W32 */
1173#endif /* FEAT_MOUSE */
1174
1175
1176#ifdef MCH_CURSOR_SHAPE
1177/*
1178 * Set the shape of the cursor.
1179 * 'thickness' can be from 1 (thin) to 99 (block)
1180 */
1181 static void
1182mch_set_cursor_shape(int thickness)
1183{
1184 CONSOLE_CURSOR_INFO ConsoleCursorInfo;
1185 ConsoleCursorInfo.dwSize = thickness;
1186 ConsoleCursorInfo.bVisible = s_cursor_visible;
1187
1188 SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo);
1189 if (s_cursor_visible)
1190 SetConsoleCursorPosition(g_hConOut, g_coord);
1191}
1192
1193 void
1194mch_update_cursor(void)
1195{
1196 int idx;
1197 int thickness;
1198
1199 /*
1200 * How the cursor is drawn depends on the current mode.
1201 */
1202 idx = get_shape_idx(FALSE);
1203
1204 if (shape_table[idx].shape == SHAPE_BLOCK)
1205 thickness = 99; /* 100 doesn't work on W95 */
1206 else
1207 thickness = shape_table[idx].percentage;
1208 mch_set_cursor_shape(thickness);
1209}
1210#endif
1211
1212#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1213/*
1214 * Handle FOCUS_EVENT.
1215 */
1216 static void
1217handle_focus_event(INPUT_RECORD ir)
1218{
1219 g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
1220 ui_focus_change((int)g_fJustGotFocus);
1221}
1222
1223/*
1224 * Wait until console input from keyboard or mouse is available,
1225 * or the time is up.
1226 * Return TRUE if something is available FALSE if not.
1227 */
1228 static int
1229WaitForChar(long msec)
1230{
1231 DWORD dwNow = 0, dwEndTime = 0;
1232 INPUT_RECORD ir;
1233 DWORD cRecords;
1234 char_u ch, ch2;
1235
1236 if (msec > 0)
1237 /* Wait until the specified time has elapsed. */
1238 dwEndTime = GetTickCount() + msec;
1239 else if (msec < 0)
1240 /* Wait forever. */
1241 dwEndTime = INFINITE;
1242
1243 /* We need to loop until the end of the time period, because
1244 * we might get multiple unusable mouse events in that time.
1245 */
1246 for (;;)
1247 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001248#ifdef FEAT_MZSCHEME
1249 mzvim_check_threads();
1250#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001251#ifdef FEAT_CLIENTSERVER
1252 serverProcessPendingMessages();
1253#endif
1254 if (0
1255#ifdef FEAT_MOUSE
1256 || g_nMouseClick != -1
1257#endif
1258#ifdef FEAT_CLIENTSERVER
1259 || input_available()
1260#endif
1261 )
1262 return TRUE;
1263
1264 if (msec > 0)
1265 {
1266 /* If the specified wait time has passed, return. */
1267 dwNow = GetTickCount();
1268 if (dwNow >= dwEndTime)
1269 break;
1270 }
1271 if (msec != 0)
1272 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001273 DWORD dwWaitTime = dwEndTime - dwNow;
1274
1275#ifdef FEAT_MZSCHEME
1276 if (mzthreads_allowed() && p_mzq > 0
1277 && (msec < 0 || (long)dwWaitTime > p_mzq))
1278 dwWaitTime = p_mzq; /* don't wait longer than 'mzquantum' */
1279#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001280#ifdef FEAT_CLIENTSERVER
1281 /* Wait for either an event on the console input or a message in
1282 * the client-server window. */
1283 if (MsgWaitForMultipleObjects(1, &g_hConIn, FALSE,
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001284 dwWaitTime, QS_SENDMESSAGE) != WAIT_OBJECT_0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001285#else
Bram Moolenaar325b7a22004-07-05 15:58:32 +00001286 if (WaitForSingleObject(g_hConIn, dwWaitTime) != WAIT_OBJECT_0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001287#endif
1288 continue;
1289 }
1290
1291 cRecords = 0;
1292 PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
1293
1294#ifdef FEAT_MBYTE_IME
1295 if (State & CMDLINE && msg_row == Rows - 1)
1296 {
1297 CONSOLE_SCREEN_BUFFER_INFO csbi;
1298
1299 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1300 {
1301 if (csbi.dwCursorPosition.Y != msg_row)
1302 {
1303 /* The screen is now messed up, must redraw the
1304 * command line and later all the windows. */
1305 redraw_all_later(CLEAR);
1306 cmdline_row -= (msg_row - csbi.dwCursorPosition.Y);
1307 redrawcmd();
1308 }
1309 }
1310 }
1311#endif
1312
1313 if (cRecords > 0)
1314 {
1315 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
1316 {
1317#ifdef FEAT_MBYTE_IME
1318 /* Windows IME sends two '\n's with only one 'ENTER'. First:
1319 * wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */
1320 if (ir.Event.KeyEvent.uChar.UnicodeChar == 0
1321 && ir.Event.KeyEvent.wVirtualKeyCode == 13)
1322 {
1323 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
1324 continue;
1325 }
1326#endif
1327 if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
1328 NULL, FALSE))
1329 return TRUE;
1330 }
1331
1332 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
1333
1334 if (ir.EventType == FOCUS_EVENT)
1335 handle_focus_event(ir);
1336 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1337 shell_resized();
1338#ifdef FEAT_MOUSE
1339 else if (ir.EventType == MOUSE_EVENT
1340 && decode_mouse_event(&ir.Event.MouseEvent))
1341 return TRUE;
1342#endif
1343 }
1344 else if (msec == 0)
1345 break;
1346 }
1347
1348#ifdef FEAT_CLIENTSERVER
1349 /* Something might have been received while we were waiting. */
1350 if (input_available())
1351 return TRUE;
1352#endif
1353 return FALSE;
1354}
1355
1356#ifndef FEAT_GUI_MSWIN
1357/*
1358 * return non-zero if a character is available
1359 */
1360 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001361mch_char_avail(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001362{
1363 return WaitForChar(0L);
1364}
1365#endif
1366
1367/*
1368 * Create the console input. Used when reading stdin doesn't work.
1369 */
1370 static void
1371create_conin(void)
1372{
1373 g_hConIn = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
1374 FILE_SHARE_READ|FILE_SHARE_WRITE,
1375 (LPSECURITY_ATTRIBUTES) NULL,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001376 OPEN_EXISTING, 0, (HANDLE)NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001377 did_create_conin = TRUE;
1378}
1379
1380/*
1381 * Get a keystroke or a mouse event
1382 */
1383 static char_u
1384tgetch(int *pmodifiers, char_u *pch2)
1385{
1386 char_u ch;
1387
1388 for (;;)
1389 {
1390 INPUT_RECORD ir;
1391 DWORD cRecords = 0;
1392
1393#ifdef FEAT_CLIENTSERVER
1394 (void)WaitForChar(-1L);
1395 if (input_available())
1396 return 0;
1397# ifdef FEAT_MOUSE
1398 if (g_nMouseClick != -1)
1399 return 0;
1400# endif
1401#endif
1402 if (ReadConsoleInput(g_hConIn, &ir, 1, &cRecords) == 0)
1403 {
1404 if (did_create_conin)
1405 read_error_exit();
1406 create_conin();
1407 continue;
1408 }
1409
1410 if (ir.EventType == KEY_EVENT)
1411 {
1412 if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2,
1413 pmodifiers, TRUE))
1414 return ch;
1415 }
1416 else if (ir.EventType == FOCUS_EVENT)
1417 handle_focus_event(ir);
1418 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1419 shell_resized();
1420#ifdef FEAT_MOUSE
1421 else if (ir.EventType == MOUSE_EVENT)
1422 {
1423 if (decode_mouse_event(&ir.Event.MouseEvent))
1424 return 0;
1425 }
1426#endif
1427 }
1428}
1429#endif /* !FEAT_GUI_W32 */
1430
1431
1432/*
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02001433 * mch_inchar(): low-level input function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434 * Get one or more characters from the keyboard or the mouse.
1435 * If time == 0, do not wait for characters.
1436 * If time == n, wait a short time for characters.
1437 * If time == -1, wait forever for characters.
1438 * Returns the number of characters read into buf.
1439 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00001440/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441 int
1442mch_inchar(
1443 char_u *buf,
1444 int maxlen,
1445 long time,
1446 int tb_change_cnt)
1447{
1448#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1449
1450 int len;
1451 int c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001452#define TYPEAHEADLEN 20
1453 static char_u typeahead[TYPEAHEADLEN]; /* previously typed bytes. */
1454 static int typeaheadlen = 0;
1455
1456 /* First use any typeahead that was kept because "buf" was too small. */
1457 if (typeaheadlen > 0)
1458 goto theend;
1459
1460#ifdef FEAT_SNIFF
1461 if (want_sniff_request)
1462 {
1463 if (sniff_request_waiting)
1464 {
1465 /* return K_SNIFF */
1466 typeahead[typeaheadlen++] = CSI;
1467 typeahead[typeaheadlen++] = (char_u)KS_EXTRA;
1468 typeahead[typeaheadlen++] = (char_u)KE_SNIFF;
1469 sniff_request_waiting = 0;
1470 want_sniff_request = 0;
1471 goto theend;
1472 }
1473 else if (time < 0 || time > 250)
1474 {
1475 /* don't wait too long, a request might be pending */
1476 time = 250;
1477 }
1478 }
1479#endif
1480
1481 if (time >= 0)
1482 {
1483 if (!WaitForChar(time)) /* no character available */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001484 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001485 }
1486 else /* time == -1, wait forever */
1487 {
1488 mch_set_winsize_now(); /* Allow winsize changes from now on */
1489
Bram Moolenaar3918c952005-03-15 22:34:55 +00001490 /*
1491 * If there is no character available within 2 seconds (default)
1492 * write the autoscript file to disk. Or cause the CursorHold event
1493 * to be triggered.
1494 */
1495 if (!WaitForChar(p_ut))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001496 {
1497#ifdef FEAT_AUTOCMD
Bram Moolenaard35f9712005-12-18 22:02:33 +00001498 if (trigger_cursorhold() && maxlen >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001499 {
Bram Moolenaar3918c952005-03-15 22:34:55 +00001500 buf[0] = K_SPECIAL;
1501 buf[1] = KS_EXTRA;
1502 buf[2] = (int)KE_CURSORHOLD;
1503 return 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001504 }
1505#endif
Bram Moolenaar702517d2005-06-27 22:34:07 +00001506 before_blocking();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001507 }
1508 }
1509
1510 /*
1511 * Try to read as many characters as there are, until the buffer is full.
1512 */
1513
1514 /* we will get at least one key. Get more if they are available. */
1515 g_fCBrkPressed = FALSE;
1516
1517#ifdef MCH_WRITE_DUMP
1518 if (fdDump)
1519 fputc('[', fdDump);
1520#endif
1521
1522 /* Keep looping until there is something in the typeahead buffer and more
1523 * to get and still room in the buffer (up to two bytes for a char and
1524 * three bytes for a modifier). */
1525 while ((typeaheadlen == 0 || WaitForChar(0L))
1526 && typeaheadlen + 5 <= TYPEAHEADLEN)
1527 {
1528 if (typebuf_changed(tb_change_cnt))
1529 {
1530 /* "buf" may be invalid now if a client put something in the
1531 * typeahead buffer and "buf" is in the typeahead buffer. */
1532 typeaheadlen = 0;
1533 break;
1534 }
1535#ifdef FEAT_MOUSE
1536 if (g_nMouseClick != -1)
1537 {
1538# ifdef MCH_WRITE_DUMP
1539 if (fdDump)
1540 fprintf(fdDump, "{%02x @ %d, %d}",
1541 g_nMouseClick, g_xMouse, g_yMouse);
1542# endif
1543 typeahead[typeaheadlen++] = ESC + 128;
1544 typeahead[typeaheadlen++] = 'M';
1545 typeahead[typeaheadlen++] = g_nMouseClick;
1546 typeahead[typeaheadlen++] = g_xMouse + '!';
1547 typeahead[typeaheadlen++] = g_yMouse + '!';
1548 g_nMouseClick = -1;
1549 }
1550 else
1551#endif
1552 {
1553 char_u ch2 = NUL;
1554 int modifiers = 0;
1555
1556 c = tgetch(&modifiers, &ch2);
1557
1558 if (typebuf_changed(tb_change_cnt))
1559 {
1560 /* "buf" may be invalid now if a client put something in the
1561 * typeahead buffer and "buf" is in the typeahead buffer. */
1562 typeaheadlen = 0;
1563 break;
1564 }
1565
1566 if (c == Ctrl_C && ctrl_c_interrupts)
1567 {
1568#if defined(FEAT_CLIENTSERVER)
1569 trash_input_buf();
1570#endif
1571 got_int = TRUE;
1572 }
1573
1574#ifdef FEAT_MOUSE
1575 if (g_nMouseClick == -1)
1576#endif
1577 {
1578 int n = 1;
1579
1580 /* A key may have one or two bytes. */
1581 typeahead[typeaheadlen] = c;
1582 if (ch2 != NUL)
1583 {
1584 typeahead[typeaheadlen + 1] = ch2;
1585 ++n;
1586 }
1587#ifdef FEAT_MBYTE
1588 /* Only convert normal characters, not special keys. Need to
1589 * convert before applying ALT, otherwise mapping <M-x> breaks
1590 * when 'tenc' is set. */
1591 if (input_conv.vc_type != CONV_NONE
1592 && (ch2 == NUL || c != K_NUL))
1593 n = convert_input(typeahead + typeaheadlen, n,
1594 TYPEAHEADLEN - typeaheadlen);
1595#endif
1596
1597 /* Use the ALT key to set the 8th bit of the character
1598 * when it's one byte, the 8th bit isn't set yet and not
1599 * using a double-byte encoding (would become a lead
1600 * byte). */
1601 if ((modifiers & MOD_MASK_ALT)
1602 && n == 1
1603 && (typeahead[typeaheadlen] & 0x80) == 0
1604#ifdef FEAT_MBYTE
1605 && !enc_dbcs
1606#endif
1607 )
1608 {
Bram Moolenaar85a3e5c2007-11-20 16:22:16 +00001609#ifdef FEAT_MBYTE
1610 n = (*mb_char2bytes)(typeahead[typeaheadlen] | 0x80,
1611 typeahead + typeaheadlen);
1612#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001613 typeahead[typeaheadlen] |= 0x80;
Bram Moolenaar85a3e5c2007-11-20 16:22:16 +00001614#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001615 modifiers &= ~MOD_MASK_ALT;
1616 }
1617
1618 if (modifiers != 0)
1619 {
1620 /* Prepend modifiers to the character. */
1621 mch_memmove(typeahead + typeaheadlen + 3,
1622 typeahead + typeaheadlen, n);
1623 typeahead[typeaheadlen++] = K_SPECIAL;
1624 typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
1625 typeahead[typeaheadlen++] = modifiers;
1626 }
1627
1628 typeaheadlen += n;
1629
1630#ifdef MCH_WRITE_DUMP
1631 if (fdDump)
1632 fputc(c, fdDump);
1633#endif
1634 }
1635 }
1636 }
1637
1638#ifdef MCH_WRITE_DUMP
1639 if (fdDump)
1640 {
1641 fputs("]\n", fdDump);
1642 fflush(fdDump);
1643 }
1644#endif
1645
Bram Moolenaar071d4272004-06-13 20:20:40 +00001646theend:
1647 /* Move typeahead to "buf", as much as fits. */
1648 len = 0;
1649 while (len < maxlen && typeaheadlen > 0)
1650 {
1651 buf[len++] = typeahead[0];
1652 mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
1653 }
1654 return len;
1655
1656#else /* FEAT_GUI_W32 */
1657 return 0;
1658#endif /* FEAT_GUI_W32 */
1659}
1660
1661#ifndef __MINGW32__
1662# include <shellapi.h> /* required for FindExecutable() */
1663#endif
1664
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001665/*
1666 * Return TRUE if "name" is in $PATH.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00001667 * TODO: Should somehow check if it's really executable.
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001668 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001669 static int
1670executable_exists(char *name)
1671{
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001672 char *dum;
1673 char fname[_MAX_PATH];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001674
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001675#ifdef FEAT_MBYTE
1676 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001677 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00001678 WCHAR *p = enc_to_utf16(name, NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001679 WCHAR fnamew[_MAX_PATH];
1680 WCHAR *dumw;
1681 long n;
1682
1683 if (p != NULL)
1684 {
1685 n = (long)SearchPathW(NULL, p, NULL, _MAX_PATH, fnamew, &dumw);
1686 vim_free(p);
1687 if (n > 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
1688 {
1689 if (n == 0)
1690 return FALSE;
1691 if (GetFileAttributesW(fnamew) & FILE_ATTRIBUTE_DIRECTORY)
1692 return FALSE;
1693 return TRUE;
1694 }
1695 /* Retry with non-wide function (for Windows 98). */
1696 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001697 }
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00001698#endif
1699 if (SearchPath(NULL, name, NULL, _MAX_PATH, fname, &dum) == 0)
1700 return FALSE;
1701 if (mch_isdir(fname))
1702 return FALSE;
1703 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001704}
1705
Bram Moolenaard32a99a2010-09-21 17:29:23 +02001706#if ((defined(__MINGW32__) || defined (__CYGWIN32__)) && \
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02001707 __MSVCRT_VERSION__ >= 0x800) || (defined(_MSC_VER) && _MSC_VER >= 1400)
Bram Moolenaard32a99a2010-09-21 17:29:23 +02001708/*
1709 * Bad parameter handler.
1710 *
1711 * Certain MS CRT functions will intentionally crash when passed invalid
1712 * parameters to highlight possible security holes. Setting this function as
1713 * the bad parameter handler will prevent the crash.
1714 *
1715 * In debug builds the parameters contain CRT information that might help track
1716 * down the source of a problem, but in non-debug builds the arguments are all
1717 * NULL/0. Debug builds will also produce assert dialogs from the CRT, it is
1718 * worth allowing these to make debugging of issues easier.
1719 */
1720 static void
1721bad_param_handler(const wchar_t *expression,
1722 const wchar_t *function,
1723 const wchar_t *file,
1724 unsigned int line,
1725 uintptr_t pReserved)
1726{
1727}
1728
1729# define SET_INVALID_PARAM_HANDLER \
1730 ((void)_set_invalid_parameter_handler(bad_param_handler))
1731#else
1732# define SET_INVALID_PARAM_HANDLER
1733#endif
1734
Bram Moolenaar071d4272004-06-13 20:20:40 +00001735#ifdef FEAT_GUI_W32
1736
1737/*
1738 * GUI version of mch_init().
1739 */
1740 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001741mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001742{
1743#ifndef __MINGW32__
1744 extern int _fmode;
1745#endif
1746
Bram Moolenaard32a99a2010-09-21 17:29:23 +02001747 /* Silently handle invalid parameters to CRT functions */
1748 SET_INVALID_PARAM_HANDLER;
1749
Bram Moolenaar071d4272004-06-13 20:20:40 +00001750 /* Let critical errors result in a failure, not in a dialog box. Required
1751 * for the timestamp test to work on removed floppies. */
1752 SetErrorMode(SEM_FAILCRITICALERRORS);
1753
1754 _fmode = O_BINARY; /* we do our own CR-LF translation */
1755
1756 /* Specify window size. Is there a place to get the default from? */
1757 Rows = 25;
1758 Columns = 80;
1759
1760 /* Look for 'vimrun' */
1761 if (!gui_is_win32s())
1762 {
1763 char_u vimrun_location[_MAX_PATH + 4];
1764
1765 /* First try in same directory as gvim.exe */
1766 STRCPY(vimrun_location, exe_name);
1767 STRCPY(gettail(vimrun_location), "vimrun.exe");
1768 if (mch_getperm(vimrun_location) >= 0)
1769 {
1770 if (*skiptowhite(vimrun_location) != NUL)
1771 {
1772 /* Enclose path with white space in double quotes. */
1773 mch_memmove(vimrun_location + 1, vimrun_location,
1774 STRLEN(vimrun_location) + 1);
1775 *vimrun_location = '"';
1776 STRCPY(gettail(vimrun_location), "vimrun\" ");
1777 }
1778 else
1779 STRCPY(gettail(vimrun_location), "vimrun ");
1780
1781 vimrun_path = (char *)vim_strsave(vimrun_location);
1782 s_dont_use_vimrun = FALSE;
1783 }
1784 else if (executable_exists("vimrun.exe"))
1785 s_dont_use_vimrun = FALSE;
1786
1787 /* Don't give the warning for a missing vimrun.exe right now, but only
1788 * when vimrun was supposed to be used. Don't bother people that do
1789 * not need vimrun.exe. */
1790 if (s_dont_use_vimrun)
1791 need_vimrun_warning = TRUE;
1792 }
1793
1794 /*
1795 * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
1796 * Otherwise the default "findstr /n" is used.
1797 */
1798 if (!executable_exists("findstr.exe"))
1799 set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
1800
1801#ifdef FEAT_CLIPBOARD
1802 clip_init(TRUE);
1803
1804 /*
Bram Moolenaar7528dd62007-05-06 12:32:12 +00001805 * Vim's own clipboard format recognises whether the text is char, line,
1806 * or rectangular block. Only useful for copying between two Vims.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001807 * "VimClipboard" was used for previous versions, using the first
1808 * character to specify MCHAR, MLINE or MBLOCK.
1809 */
1810 clip_star.format = RegisterClipboardFormat("VimClipboard2");
1811 clip_star.format_raw = RegisterClipboardFormat("VimRawBytes");
1812#endif
1813}
1814
1815
1816#else /* FEAT_GUI_W32 */
1817
1818#define SRWIDTH(sr) ((sr).Right - (sr).Left + 1)
1819#define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1)
1820
1821/*
1822 * ClearConsoleBuffer()
1823 * Description:
1824 * Clears the entire contents of the console screen buffer, using the
1825 * specified attribute.
1826 * Returns:
1827 * TRUE on success
1828 */
1829 static BOOL
1830ClearConsoleBuffer(WORD wAttribute)
1831{
1832 CONSOLE_SCREEN_BUFFER_INFO csbi;
1833 COORD coord;
1834 DWORD NumCells, dummy;
1835
1836 if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1837 return FALSE;
1838
1839 NumCells = csbi.dwSize.X * csbi.dwSize.Y;
1840 coord.X = 0;
1841 coord.Y = 0;
1842 if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells,
1843 coord, &dummy))
1844 {
1845 return FALSE;
1846 }
1847 if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells,
1848 coord, &dummy))
1849 {
1850 return FALSE;
1851 }
1852
1853 return TRUE;
1854}
1855
1856/*
1857 * FitConsoleWindow()
1858 * Description:
1859 * Checks if the console window will fit within given buffer dimensions.
1860 * Also, if requested, will shrink the window to fit.
1861 * Returns:
1862 * TRUE on success
1863 */
1864 static BOOL
1865FitConsoleWindow(
1866 COORD dwBufferSize,
1867 BOOL WantAdjust)
1868{
1869 CONSOLE_SCREEN_BUFFER_INFO csbi;
1870 COORD dwWindowSize;
1871 BOOL NeedAdjust = FALSE;
1872
1873 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1874 {
1875 /*
1876 * A buffer resize will fail if the current console window does
1877 * not lie completely within that buffer. To avoid this, we might
1878 * have to move and possibly shrink the window.
1879 */
1880 if (csbi.srWindow.Right >= dwBufferSize.X)
1881 {
1882 dwWindowSize.X = SRWIDTH(csbi.srWindow);
1883 if (dwWindowSize.X > dwBufferSize.X)
1884 dwWindowSize.X = dwBufferSize.X;
1885 csbi.srWindow.Right = dwBufferSize.X - 1;
1886 csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X;
1887 NeedAdjust = TRUE;
1888 }
1889 if (csbi.srWindow.Bottom >= dwBufferSize.Y)
1890 {
1891 dwWindowSize.Y = SRHEIGHT(csbi.srWindow);
1892 if (dwWindowSize.Y > dwBufferSize.Y)
1893 dwWindowSize.Y = dwBufferSize.Y;
1894 csbi.srWindow.Bottom = dwBufferSize.Y - 1;
1895 csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y;
1896 NeedAdjust = TRUE;
1897 }
1898 if (NeedAdjust && WantAdjust)
1899 {
1900 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow))
1901 return FALSE;
1902 }
1903 return TRUE;
1904 }
1905
1906 return FALSE;
1907}
1908
1909typedef struct ConsoleBufferStruct
1910{
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001911 BOOL IsValid;
1912 CONSOLE_SCREEN_BUFFER_INFO Info;
1913 PCHAR_INFO Buffer;
1914 COORD BufferSize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001915} ConsoleBuffer;
1916
1917/*
1918 * SaveConsoleBuffer()
1919 * Description:
1920 * Saves important information about the console buffer, including the
1921 * actual buffer contents. The saved information is suitable for later
1922 * restoration by RestoreConsoleBuffer().
1923 * Returns:
1924 * TRUE if all information was saved; FALSE otherwise
1925 * If FALSE, still sets cb->IsValid if buffer characteristics were saved.
1926 */
1927 static BOOL
1928SaveConsoleBuffer(
1929 ConsoleBuffer *cb)
1930{
1931 DWORD NumCells;
1932 COORD BufferCoord;
1933 SMALL_RECT ReadRegion;
1934 WORD Y, Y_incr;
1935
1936 if (cb == NULL)
1937 return FALSE;
1938
1939 if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
1940 {
1941 cb->IsValid = FALSE;
1942 return FALSE;
1943 }
1944 cb->IsValid = TRUE;
1945
1946 /*
1947 * Allocate a buffer large enough to hold the entire console screen
1948 * buffer. If this ConsoleBuffer structure has already been initialized
1949 * with a buffer of the correct size, then just use that one.
1950 */
1951 if (!cb->IsValid || cb->Buffer == NULL ||
1952 cb->BufferSize.X != cb->Info.dwSize.X ||
1953 cb->BufferSize.Y != cb->Info.dwSize.Y)
1954 {
1955 cb->BufferSize.X = cb->Info.dwSize.X;
1956 cb->BufferSize.Y = cb->Info.dwSize.Y;
1957 NumCells = cb->BufferSize.X * cb->BufferSize.Y;
Bram Moolenaar3c2d6532011-02-01 13:48:53 +01001958 vim_free(cb->Buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001959 cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
1960 if (cb->Buffer == NULL)
1961 return FALSE;
1962 }
1963
1964 /*
1965 * We will now copy the console screen buffer into our buffer.
1966 * ReadConsoleOutput() seems to be limited as far as how much you
1967 * can read at a time. Empirically, this number seems to be about
1968 * 12000 cells (rows * columns). Start at position (0, 0) and copy
1969 * in chunks until it is all copied. The chunks will all have the
1970 * same horizontal characteristics, so initialize them now. The
1971 * height of each chunk will be (12000 / width).
1972 */
1973 BufferCoord.X = 0;
1974 ReadRegion.Left = 0;
1975 ReadRegion.Right = cb->Info.dwSize.X - 1;
1976 Y_incr = 12000 / cb->Info.dwSize.X;
1977 for (Y = 0; Y < cb->BufferSize.Y; Y += Y_incr)
1978 {
1979 /*
1980 * Read into position (0, Y) in our buffer.
1981 */
1982 BufferCoord.Y = Y;
1983 /*
1984 * Read the region whose top left corner is (0, Y) and whose bottom
1985 * right corner is (width - 1, Y + Y_incr - 1). This should define
1986 * a region of size width by Y_incr. Don't worry if this region is
1987 * too large for the remaining buffer; it will be cropped.
1988 */
1989 ReadRegion.Top = Y;
1990 ReadRegion.Bottom = Y + Y_incr - 1;
1991 if (!ReadConsoleOutput(g_hConOut, /* output handle */
1992 cb->Buffer, /* our buffer */
1993 cb->BufferSize, /* dimensions of our buffer */
1994 BufferCoord, /* offset in our buffer */
1995 &ReadRegion)) /* region to save */
1996 {
1997 vim_free(cb->Buffer);
1998 cb->Buffer = NULL;
1999 return FALSE;
2000 }
2001 }
2002
2003 return TRUE;
2004}
2005
2006/*
2007 * RestoreConsoleBuffer()
2008 * Description:
2009 * Restores important information about the console buffer, including the
2010 * actual buffer contents, if desired. The information to restore is in
2011 * the same format used by SaveConsoleBuffer().
2012 * Returns:
2013 * TRUE on success
2014 */
2015 static BOOL
2016RestoreConsoleBuffer(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002017 ConsoleBuffer *cb,
2018 BOOL RestoreScreen)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002019{
2020 COORD BufferCoord;
2021 SMALL_RECT WriteRegion;
2022
2023 if (cb == NULL || !cb->IsValid)
2024 return FALSE;
2025
2026 /*
2027 * Before restoring the buffer contents, clear the current buffer, and
2028 * restore the cursor position and window information. Doing this now
2029 * prevents old buffer contents from "flashing" onto the screen.
2030 */
2031 if (RestoreScreen)
2032 ClearConsoleBuffer(cb->Info.wAttributes);
2033
2034 FitConsoleWindow(cb->Info.dwSize, TRUE);
2035 if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
2036 return FALSE;
2037 if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
2038 return FALSE;
2039
2040 if (!RestoreScreen)
2041 {
2042 /*
2043 * No need to restore the screen buffer contents, so we're done.
2044 */
2045 return TRUE;
2046 }
2047
2048 if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
2049 return FALSE;
2050 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
2051 return FALSE;
2052
2053 /*
2054 * Restore the screen buffer contents.
2055 */
2056 if (cb->Buffer != NULL)
2057 {
2058 BufferCoord.X = 0;
2059 BufferCoord.Y = 0;
2060 WriteRegion.Left = 0;
2061 WriteRegion.Top = 0;
2062 WriteRegion.Right = cb->Info.dwSize.X - 1;
2063 WriteRegion.Bottom = cb->Info.dwSize.Y - 1;
2064 if (!WriteConsoleOutput(g_hConOut, /* output handle */
2065 cb->Buffer, /* our buffer */
2066 cb->BufferSize, /* dimensions of our buffer */
2067 BufferCoord, /* offset in our buffer */
2068 &WriteRegion)) /* region to restore */
2069 {
2070 return FALSE;
2071 }
2072 }
2073
2074 return TRUE;
2075}
2076
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002077#define FEAT_RESTORE_ORIG_SCREEN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078#ifdef FEAT_RESTORE_ORIG_SCREEN
2079static ConsoleBuffer g_cbOrig = { 0 };
2080#endif
2081static ConsoleBuffer g_cbNonTermcap = { 0 };
2082static ConsoleBuffer g_cbTermcap = { 0 };
2083
2084#ifdef FEAT_TITLE
2085#ifdef __BORLANDC__
2086typedef HWND (__stdcall *GETCONSOLEWINDOWPROC)(VOID);
2087#else
2088typedef WINBASEAPI HWND (WINAPI *GETCONSOLEWINDOWPROC)(VOID);
2089#endif
2090char g_szOrigTitle[256] = { 0 };
2091HWND g_hWnd = NULL; /* also used in os_mswin.c */
2092static HICON g_hOrigIconSmall = NULL;
2093static HICON g_hOrigIcon = NULL;
2094static HICON g_hVimIcon = NULL;
2095static BOOL g_fCanChangeIcon = FALSE;
2096
2097/* ICON* are not defined in VC++ 4.0 */
2098#ifndef ICON_SMALL
2099#define ICON_SMALL 0
2100#endif
2101#ifndef ICON_BIG
2102#define ICON_BIG 1
2103#endif
2104/*
2105 * GetConsoleIcon()
2106 * Description:
2107 * Attempts to retrieve the small icon and/or the big icon currently in
2108 * use by a given window.
2109 * Returns:
2110 * TRUE on success
2111 */
2112 static BOOL
2113GetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002114 HWND hWnd,
2115 HICON *phIconSmall,
2116 HICON *phIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002117{
2118 if (hWnd == NULL)
2119 return FALSE;
2120
2121 if (phIconSmall != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002122 *phIconSmall = (HICON)SendMessage(hWnd, WM_GETICON,
2123 (WPARAM)ICON_SMALL, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002124 if (phIcon != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002125 *phIcon = (HICON)SendMessage(hWnd, WM_GETICON,
2126 (WPARAM)ICON_BIG, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002127 return TRUE;
2128}
2129
2130/*
2131 * SetConsoleIcon()
2132 * Description:
2133 * Attempts to change the small icon and/or the big icon currently in
2134 * use by a given window.
2135 * Returns:
2136 * TRUE on success
2137 */
2138 static BOOL
2139SetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002140 HWND hWnd,
2141 HICON hIconSmall,
2142 HICON hIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002143{
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002144 HICON hPrevIconSmall;
2145 HICON hPrevIcon;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002146
2147 if (hWnd == NULL)
2148 return FALSE;
2149
2150 if (hIconSmall != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002151 hPrevIconSmall = (HICON)SendMessage(hWnd, WM_SETICON,
2152 (WPARAM)ICON_SMALL, (LPARAM)hIconSmall);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002153 if (hIcon != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002154 hPrevIcon = (HICON)SendMessage(hWnd, WM_SETICON,
2155 (WPARAM)ICON_BIG,(LPARAM) hIcon);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002156 return TRUE;
2157}
2158
2159/*
2160 * SaveConsoleTitleAndIcon()
2161 * Description:
2162 * Saves the current console window title in g_szOrigTitle, for later
2163 * restoration. Also, attempts to obtain a handle to the console window,
2164 * and use it to save the small and big icons currently in use by the
2165 * console window. This is not always possible on some versions of Windows;
2166 * nor is it possible when running Vim remotely using Telnet (since the
2167 * console window the user sees is owned by a remote process).
2168 */
2169 static void
2170SaveConsoleTitleAndIcon(void)
2171{
2172 GETCONSOLEWINDOWPROC GetConsoleWindowProc;
2173
2174 /* Save the original title. */
2175 if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle)))
2176 return;
2177
2178 /*
2179 * Obtain a handle to the console window using GetConsoleWindow() from
2180 * KERNEL32.DLL; we need to handle in order to change the window icon.
2181 * This function only exists on NT-based Windows, starting with Windows
2182 * 2000. On older operating systems, we can't change the window icon
2183 * anyway.
2184 */
2185 if ((GetConsoleWindowProc = (GETCONSOLEWINDOWPROC)
2186 GetProcAddress(GetModuleHandle("KERNEL32.DLL"),
2187 "GetConsoleWindow")) != NULL)
2188 {
2189 g_hWnd = (*GetConsoleWindowProc)();
2190 }
2191 if (g_hWnd == NULL)
2192 return;
2193
2194 /* Save the original console window icon. */
2195 GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon);
2196 if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL)
2197 return;
2198
2199 /* Extract the first icon contained in the Vim executable. */
2200 g_hVimIcon = ExtractIcon(NULL, exe_name, 0);
2201 if (g_hVimIcon != NULL)
2202 g_fCanChangeIcon = TRUE;
2203}
2204#endif
2205
2206static int g_fWindInitCalled = FALSE;
2207static int g_fTermcapMode = FALSE;
2208static CONSOLE_CURSOR_INFO g_cci;
2209static DWORD g_cmodein = 0;
2210static DWORD g_cmodeout = 0;
2211
2212/*
2213 * non-GUI version of mch_init().
2214 */
2215 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002216mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002217{
2218#ifndef FEAT_RESTORE_ORIG_SCREEN
2219 CONSOLE_SCREEN_BUFFER_INFO csbi;
2220#endif
2221#ifndef __MINGW32__
2222 extern int _fmode;
2223#endif
2224
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002225 /* Silently handle invalid parameters to CRT functions */
2226 SET_INVALID_PARAM_HANDLER;
2227
Bram Moolenaar071d4272004-06-13 20:20:40 +00002228 /* Let critical errors result in a failure, not in a dialog box. Required
2229 * for the timestamp test to work on removed floppies. */
2230 SetErrorMode(SEM_FAILCRITICALERRORS);
2231
2232 _fmode = O_BINARY; /* we do our own CR-LF translation */
2233 out_flush();
2234
2235 /* Obtain handles for the standard Console I/O devices */
2236 if (read_cmd_fd == 0)
2237 g_hConIn = GetStdHandle(STD_INPUT_HANDLE);
2238 else
2239 create_conin();
2240 g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
2241
2242#ifdef FEAT_RESTORE_ORIG_SCREEN
2243 /* Save the initial console buffer for later restoration */
2244 SaveConsoleBuffer(&g_cbOrig);
2245 g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
2246#else
2247 /* Get current text attributes */
2248 GetConsoleScreenBufferInfo(g_hConOut, &csbi);
2249 g_attrCurrent = g_attrDefault = csbi.wAttributes;
2250#endif
2251 if (cterm_normal_fg_color == 0)
2252 cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
2253 if (cterm_normal_bg_color == 0)
2254 cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
2255
2256 /* set termcap codes to current text attributes */
2257 update_tcap(g_attrCurrent);
2258
2259 GetConsoleCursorInfo(g_hConOut, &g_cci);
2260 GetConsoleMode(g_hConIn, &g_cmodein);
2261 GetConsoleMode(g_hConOut, &g_cmodeout);
2262
2263#ifdef FEAT_TITLE
2264 SaveConsoleTitleAndIcon();
2265 /*
2266 * Set both the small and big icons of the console window to Vim's icon.
2267 * Note that Vim presently only has one size of icon (32x32), but it
2268 * automatically gets scaled down to 16x16 when setting the small icon.
2269 */
2270 if (g_fCanChangeIcon)
2271 SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon);
2272#endif
2273
2274 ui_get_shellsize();
2275
2276#ifdef MCH_WRITE_DUMP
2277 fdDump = fopen("dump", "wt");
2278
2279 if (fdDump)
2280 {
2281 time_t t;
2282
2283 time(&t);
2284 fputs(ctime(&t), fdDump);
2285 fflush(fdDump);
2286 }
2287#endif
2288
2289 g_fWindInitCalled = TRUE;
2290
2291#ifdef FEAT_MOUSE
2292 g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
2293#endif
2294
2295#ifdef FEAT_CLIPBOARD
2296 clip_init(TRUE);
2297
2298 /*
2299 * Vim's own clipboard format recognises whether the text is char, line, or
2300 * rectangular block. Only useful for copying between two Vims.
2301 * "VimClipboard" was used for previous versions, using the first
2302 * character to specify MCHAR, MLINE or MBLOCK.
2303 */
2304 clip_star.format = RegisterClipboardFormat("VimClipboard2");
2305 clip_star.format_raw = RegisterClipboardFormat("VimRawBytes");
2306#endif
2307
2308 /* This will be NULL on anything but NT 4.0 */
2309 s_pfnGetConsoleKeyboardLayoutName =
2310 (PFNGCKLN) GetProcAddress(GetModuleHandle("kernel32.dll"),
2311 "GetConsoleKeyboardLayoutNameA");
2312}
2313
2314/*
2315 * non-GUI version of mch_exit().
2316 * Shut down and exit with status `r'
2317 * Careful: mch_exit() may be called before mch_init()!
2318 */
2319 void
2320mch_exit(int r)
2321{
2322 stoptermcap();
2323
2324 if (g_fWindInitCalled)
2325 settmode(TMODE_COOK);
2326
2327 ml_close_all(TRUE); /* remove all memfiles */
2328
2329 if (g_fWindInitCalled)
2330 {
2331#ifdef FEAT_TITLE
2332 mch_restore_title(3);
2333 /*
2334 * Restore both the small and big icons of the console window to
2335 * what they were at startup. Don't do this when the window is
2336 * closed, Vim would hang here.
2337 */
2338 if (g_fCanChangeIcon && !g_fForceExit)
2339 SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon);
2340#endif
2341
2342#ifdef MCH_WRITE_DUMP
2343 if (fdDump)
2344 {
2345 time_t t;
2346
2347 time(&t);
2348 fputs(ctime(&t), fdDump);
2349 fclose(fdDump);
2350 }
2351 fdDump = NULL;
2352#endif
2353 }
2354
2355 SetConsoleCursorInfo(g_hConOut, &g_cci);
2356 SetConsoleMode(g_hConIn, g_cmodein);
2357 SetConsoleMode(g_hConOut, g_cmodeout);
2358
2359#ifdef DYNAMIC_GETTEXT
2360 dyn_libintl_end();
2361#endif
2362
2363 exit(r);
2364}
2365#endif /* !FEAT_GUI_W32 */
2366
Bram Moolenaar071d4272004-06-13 20:20:40 +00002367/*
2368 * Do we have an interactive window?
2369 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00002370/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00002371 int
2372mch_check_win(
2373 int argc,
2374 char **argv)
2375{
2376 get_exe_name();
2377
2378#ifdef FEAT_GUI_W32
2379 return OK; /* GUI always has a tty */
2380#else
2381 if (isatty(1))
2382 return OK;
2383 return FAIL;
2384#endif
2385}
2386
2387
2388/*
2389 * fname_case(): Set the case of the file name, if it already exists.
2390 * When "len" is > 0, also expand short to long filenames.
2391 */
2392 void
2393fname_case(
2394 char_u *name,
2395 int len)
2396{
2397 char szTrueName[_MAX_PATH + 2];
Bram Moolenaar464c9252010-10-13 20:37:41 +02002398 char szTrueNameTemp[_MAX_PATH + 2];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002399 char *ptrue, *ptruePrev;
2400 char *porig, *porigPrev;
2401 int flen;
2402 WIN32_FIND_DATA fb;
2403 HANDLE hFind;
2404 int c;
Bram Moolenaar464c9252010-10-13 20:37:41 +02002405 int slen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002406
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +00002407 flen = (int)STRLEN(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002408 if (flen == 0 || flen > _MAX_PATH)
2409 return;
2410
2411 slash_adjust(name);
2412
2413 /* Build the new name in szTrueName[] one component at a time. */
2414 porig = name;
2415 ptrue = szTrueName;
2416
2417 if (isalpha(porig[0]) && porig[1] == ':')
2418 {
2419 /* copy leading drive letter */
2420 *ptrue++ = *porig++;
2421 *ptrue++ = *porig++;
2422 *ptrue = NUL; /* in case nothing follows */
2423 }
2424
2425 while (*porig != NUL)
2426 {
2427 /* copy \ characters */
2428 while (*porig == psepc)
2429 *ptrue++ = *porig++;
2430
2431 ptruePrev = ptrue;
2432 porigPrev = porig;
2433 while (*porig != NUL && *porig != psepc)
2434 {
2435#ifdef FEAT_MBYTE
2436 int l;
2437
2438 if (enc_dbcs)
2439 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00002440 l = (*mb_ptr2len)(porig);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002441 while (--l >= 0)
2442 *ptrue++ = *porig++;
2443 }
2444 else
2445#endif
2446 *ptrue++ = *porig++;
2447 }
2448 *ptrue = NUL;
2449
Bram Moolenaar464c9252010-10-13 20:37:41 +02002450 /* To avoid a slow failure append "\*" when searching a directory,
2451 * server or network share. */
2452 STRCPY(szTrueNameTemp, szTrueName);
Bram Moolenaar6b5ef062010-10-27 12:18:00 +02002453 slen = (int)strlen(szTrueNameTemp);
Bram Moolenaar464c9252010-10-13 20:37:41 +02002454 if (*porig == psepc && slen + 2 < _MAX_PATH)
2455 STRCPY(szTrueNameTemp + slen, "\\*");
2456
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 /* Skip "", "." and "..". */
2458 if (ptrue > ptruePrev
2459 && (ptruePrev[0] != '.'
2460 || (ptruePrev[1] != NUL
2461 && (ptruePrev[1] != '.' || ptruePrev[2] != NUL)))
Bram Moolenaar464c9252010-10-13 20:37:41 +02002462 && (hFind = FindFirstFile(szTrueNameTemp, &fb))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002463 != INVALID_HANDLE_VALUE)
2464 {
2465 c = *porig;
2466 *porig = NUL;
2467
2468 /* Only use the match when it's the same name (ignoring case) or
2469 * expansion is allowed and there is a match with the short name
2470 * and there is enough room. */
2471 if (_stricoll(porigPrev, fb.cFileName) == 0
2472 || (len > 0
2473 && (_stricoll(porigPrev, fb.cAlternateFileName) == 0
2474 && (int)(ptruePrev - szTrueName)
2475 + (int)strlen(fb.cFileName) < len)))
2476 {
2477 STRCPY(ptruePrev, fb.cFileName);
2478
2479 /* Look for exact match and prefer it if found. Must be a
2480 * long name, otherwise there would be only one match. */
2481 while (FindNextFile(hFind, &fb))
2482 {
2483 if (*fb.cAlternateFileName != NUL
2484 && (strcoll(porigPrev, fb.cFileName) == 0
2485 || (len > 0
2486 && (_stricoll(porigPrev,
2487 fb.cAlternateFileName) == 0
2488 && (int)(ptruePrev - szTrueName)
2489 + (int)strlen(fb.cFileName) < len))))
2490 {
2491 STRCPY(ptruePrev, fb.cFileName);
2492 break;
2493 }
2494 }
2495 }
2496 FindClose(hFind);
2497 *porig = c;
2498 ptrue = ptruePrev + strlen(ptruePrev);
2499 }
2500 }
2501
2502 STRCPY(name, szTrueName);
2503}
2504
2505
2506/*
2507 * Insert user name in s[len].
2508 */
2509 int
2510mch_get_user_name(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002511 char_u *s,
2512 int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002513{
Bram Moolenaar41a09032007-10-01 18:34:34 +00002514 char szUserName[256 + 1]; /* UNLEN is 256 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002515 DWORD cch = sizeof szUserName;
2516
2517 if (GetUserName(szUserName, &cch))
2518 {
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00002519 vim_strncpy(s, szUserName, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002520 return OK;
2521 }
2522 s[0] = NUL;
2523 return FAIL;
2524}
2525
2526
2527/*
2528 * Insert host name in s[len].
2529 */
2530 void
2531mch_get_host_name(
2532 char_u *s,
2533 int len)
2534{
2535 DWORD cch = len;
2536
2537 if (!GetComputerName(s, &cch))
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00002538 vim_strncpy(s, "PC (Win32 Vim)", len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002539}
2540
2541
2542/*
2543 * return process ID
2544 */
2545 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002546mch_get_pid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002547{
2548 return (long)GetCurrentProcessId();
2549}
2550
2551
2552/*
2553 * Get name of current directory into buffer 'buf' of length 'len' bytes.
2554 * Return OK for success, FAIL for failure.
2555 */
2556 int
2557mch_dirname(
2558 char_u *buf,
2559 int len)
2560{
2561 /*
2562 * Originally this was:
2563 * return (getcwd(buf, len) != NULL ? OK : FAIL);
2564 * But the Win32s known bug list says that getcwd() doesn't work
2565 * so use the Win32 system call instead. <Negri>
2566 */
2567#ifdef FEAT_MBYTE
2568 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2569 {
2570 WCHAR wbuf[_MAX_PATH + 1];
2571
2572 if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0)
2573 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00002574 char_u *p = utf16_to_enc(wbuf, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002575
2576 if (p != NULL)
2577 {
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00002578 vim_strncpy(buf, p, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002579 vim_free(p);
2580 return OK;
2581 }
2582 }
2583 /* Retry with non-wide function (for Windows 98). */
2584 }
2585#endif
2586 return (GetCurrentDirectory(len, buf) != 0 ? OK : FAIL);
2587}
2588
2589/*
2590 * get file permissions for `name'
2591 * -1 : error
2592 * else FILE_ATTRIBUTE_* defined in winnt.h
2593 */
2594 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002595mch_getperm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002596{
2597#ifdef FEAT_MBYTE
2598 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2599 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00002600 WCHAR *p = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002601 long n;
2602
2603 if (p != NULL)
2604 {
2605 n = (long)GetFileAttributesW(p);
2606 vim_free(p);
2607 if (n >= 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
2608 return n;
2609 /* Retry with non-wide function (for Windows 98). */
2610 }
2611 }
2612#endif
2613 return (long)GetFileAttributes((char *)name);
2614}
2615
2616
2617/*
2618 * set file permission for `name' to `perm'
2619 */
2620 int
2621mch_setperm(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002622 char_u *name,
2623 long perm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002624{
2625 perm |= FILE_ATTRIBUTE_ARCHIVE; /* file has changed, set archive bit */
2626#ifdef FEAT_MBYTE
2627 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2628 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00002629 WCHAR *p = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002630 long n;
2631
2632 if (p != NULL)
2633 {
2634 n = (long)SetFileAttributesW(p, perm);
2635 vim_free(p);
2636 if (n || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
2637 return n ? OK : FAIL;
2638 /* Retry with non-wide function (for Windows 98). */
2639 }
2640 }
2641#endif
2642 return SetFileAttributes((char *)name, perm) ? OK : FAIL;
2643}
2644
2645/*
2646 * Set hidden flag for "name".
2647 */
2648 void
2649mch_hide(char_u *name)
2650{
2651 int perm;
2652#ifdef FEAT_MBYTE
2653 WCHAR *p = NULL;
2654
2655 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar36f692d2008-11-20 16:10:17 +00002656 p = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002657#endif
2658
2659#ifdef FEAT_MBYTE
2660 if (p != NULL)
2661 {
2662 perm = GetFileAttributesW(p);
2663 if (perm < 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2664 {
2665 /* Retry with non-wide function (for Windows 98). */
2666 vim_free(p);
2667 p = NULL;
2668 }
2669 }
2670 if (p == NULL)
2671#endif
2672 perm = GetFileAttributes((char *)name);
2673 if (perm >= 0)
2674 {
2675 perm |= FILE_ATTRIBUTE_HIDDEN;
2676#ifdef FEAT_MBYTE
2677 if (p != NULL)
2678 {
2679 if (SetFileAttributesW(p, perm) == 0
2680 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2681 {
2682 /* Retry with non-wide function (for Windows 98). */
2683 vim_free(p);
2684 p = NULL;
2685 }
2686 }
2687 if (p == NULL)
2688#endif
2689 SetFileAttributes((char *)name, perm);
2690 }
2691#ifdef FEAT_MBYTE
2692 vim_free(p);
2693#endif
2694}
2695
2696/*
2697 * return TRUE if "name" is a directory
2698 * return FALSE if "name" is not a directory or upon error
2699 */
2700 int
2701mch_isdir(char_u *name)
2702{
2703 int f = mch_getperm(name);
2704
2705 if (f == -1)
2706 return FALSE; /* file does not exist at all */
2707
2708 return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
2709}
2710
2711/*
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02002712 * Create directory "name".
2713 * Return 0 on success, -1 on error.
2714 */
2715 int
2716mch_mkdir(char_u *name)
2717{
2718#ifdef FEAT_MBYTE
2719 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2720 {
2721 WCHAR *p;
2722 int retval;
2723
2724 p = enc_to_utf16(name, NULL);
2725 if (p == NULL)
2726 return -1;
2727 retval = _wmkdir(p);
2728 vim_free(p);
2729 return retval;
2730 }
2731#endif
2732 return _mkdir(name);
2733}
2734
2735/*
Bram Moolenaar03f48552006-02-28 23:52:23 +00002736 * Return TRUE if file "fname" has more than one link.
2737 */
2738 int
2739mch_is_linked(char_u *fname)
2740{
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02002741 BY_HANDLE_FILE_INFORMATION info;
2742
2743 return win32_fileinfo(fname, &info) == FILEINFO_OK
2744 && info.nNumberOfLinks > 1;
2745}
2746
2747/*
2748 * Get the by-handle-file-information for "fname".
2749 * Returns FILEINFO_OK when OK.
2750 * returns FILEINFO_ENC_FAIL when enc_to_utf16() failed.
2751 * Returns FILEINFO_READ_FAIL when CreateFile() failed.
2752 * Returns FILEINFO_INFO_FAIL when GetFileInformationByHandle() failed.
2753 */
2754 int
2755win32_fileinfo(char_u *fname, BY_HANDLE_FILE_INFORMATION *info)
2756{
Bram Moolenaar03f48552006-02-28 23:52:23 +00002757 HANDLE hFile;
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02002758 int res = FILEINFO_READ_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00002759#ifdef FEAT_MBYTE
2760 WCHAR *wn = NULL;
2761
2762 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02002763 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00002764 wn = enc_to_utf16(fname, NULL);
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02002765 if (wn == NULL)
2766 res = FILEINFO_ENC_FAIL;
2767 }
Bram Moolenaar03f48552006-02-28 23:52:23 +00002768 if (wn != NULL)
2769 {
2770 hFile = CreateFileW(wn, /* file name */
2771 GENERIC_READ, /* access mode */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02002772 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
Bram Moolenaar03f48552006-02-28 23:52:23 +00002773 NULL, /* security descriptor */
2774 OPEN_EXISTING, /* creation disposition */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02002775 FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
Bram Moolenaar03f48552006-02-28 23:52:23 +00002776 NULL); /* handle to template file */
2777 if (hFile == INVALID_HANDLE_VALUE
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02002778 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
Bram Moolenaar03f48552006-02-28 23:52:23 +00002779 {
2780 /* Retry with non-wide function (for Windows 98). */
2781 vim_free(wn);
2782 wn = NULL;
2783 }
2784 }
2785 if (wn == NULL)
2786#endif
2787 hFile = CreateFile(fname, /* file name */
2788 GENERIC_READ, /* access mode */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02002789 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
Bram Moolenaar03f48552006-02-28 23:52:23 +00002790 NULL, /* security descriptor */
2791 OPEN_EXISTING, /* creation disposition */
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02002792 FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
Bram Moolenaar03f48552006-02-28 23:52:23 +00002793 NULL); /* handle to template file */
2794
2795 if (hFile != INVALID_HANDLE_VALUE)
2796 {
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02002797 if (GetFileInformationByHandle(hFile, info) != 0)
2798 res = FILEINFO_OK;
2799 else
2800 res = FILEINFO_INFO_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00002801 CloseHandle(hFile);
2802 }
2803
2804#ifdef FEAT_MBYTE
2805 vim_free(wn);
2806#endif
2807 return res;
2808}
2809
2810/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002811 * Return TRUE if file or directory "name" is writable (not readonly).
2812 * Strange semantics of Win32: a readonly directory is writable, but you can't
2813 * delete a file. Let's say this means it is writable.
2814 */
2815 int
2816mch_writable(char_u *name)
2817{
2818 int perm = mch_getperm(name);
2819
2820 return (perm != -1 && (!(perm & FILE_ATTRIBUTE_READONLY)
2821 || (perm & FILE_ATTRIBUTE_DIRECTORY)));
2822}
2823
Bram Moolenaar071d4272004-06-13 20:20:40 +00002824/*
2825 * Return 1 if "name" can be executed, 0 if not.
2826 * Return -1 if unknown.
2827 */
2828 int
2829mch_can_exe(char_u *name)
2830{
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00002831 char_u buf[_MAX_PATH];
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002832 int len = (int)STRLEN(name);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00002833 char_u *p;
2834
2835 if (len >= _MAX_PATH) /* safety check */
2836 return FALSE;
2837
2838 /* If there already is an extension try using the name directly. Also do
2839 * this with a Unix-shell like 'shell'. */
2840 if (vim_strchr(gettail(name), '.') != NULL
2841 || strstr((char *)gettail(p_sh), "sh") != NULL)
2842 if (executable_exists((char *)name))
2843 return TRUE;
2844
2845 /*
2846 * Loop over all extensions in $PATHEXT.
2847 */
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00002848 vim_strncpy(buf, name, _MAX_PATH - 1);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00002849 p = mch_getenv("PATHEXT");
2850 if (p == NULL)
2851 p = (char_u *)".com;.exe;.bat;.cmd";
2852 while (*p)
2853 {
2854 if (p[0] == '.' && (p[1] == NUL || p[1] == ';'))
2855 {
2856 /* A single "." means no extension is added. */
2857 buf[len] = NUL;
2858 ++p;
2859 if (*p)
2860 ++p;
2861 }
2862 else
2863 copy_option_part(&p, buf + len, _MAX_PATH - len, ";");
2864 if (executable_exists((char *)buf))
2865 return TRUE;
2866 }
2867 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002868}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002869
2870/*
2871 * Check what "name" is:
2872 * NODE_NORMAL: file or directory (or doesn't exist)
2873 * NODE_WRITABLE: writable device, socket, fifo, etc.
2874 * NODE_OTHER: non-writable things
2875 */
2876 int
2877mch_nodetype(char_u *name)
2878{
2879 HANDLE hFile;
2880 int type;
2881
Bram Moolenaar043545e2006-10-10 16:44:07 +00002882 /* We can't open a file with a name "\\.\con" or "\\.\prn" and trying to
2883 * read from it later will cause Vim to hang. Thus return NODE_WRITABLE
2884 * here. */
2885 if (STRNCMP(name, "\\\\.\\", 4) == 0)
2886 return NODE_WRITABLE;
2887
Bram Moolenaar071d4272004-06-13 20:20:40 +00002888 hFile = CreateFile(name, /* file name */
2889 GENERIC_WRITE, /* access mode */
2890 0, /* share mode */
2891 NULL, /* security descriptor */
2892 OPEN_EXISTING, /* creation disposition */
2893 0, /* file attributes */
2894 NULL); /* handle to template file */
2895
2896 if (hFile == INVALID_HANDLE_VALUE)
2897 return NODE_NORMAL;
2898
2899 type = GetFileType(hFile);
2900 CloseHandle(hFile);
2901 if (type == FILE_TYPE_CHAR)
2902 return NODE_WRITABLE;
2903 if (type == FILE_TYPE_DISK)
2904 return NODE_NORMAL;
2905 return NODE_OTHER;
2906}
2907
2908#ifdef HAVE_ACL
2909struct my_acl
2910{
2911 PSECURITY_DESCRIPTOR pSecurityDescriptor;
2912 PSID pSidOwner;
2913 PSID pSidGroup;
2914 PACL pDacl;
2915 PACL pSacl;
2916};
2917#endif
2918
2919/*
2920 * Return a pointer to the ACL of file "fname" in allocated memory.
2921 * Return NULL if the ACL is not available for whatever reason.
2922 */
2923 vim_acl_T
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002924mch_get_acl(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002925{
2926#ifndef HAVE_ACL
2927 return (vim_acl_T)NULL;
2928#else
2929 struct my_acl *p = NULL;
2930
2931 /* This only works on Windows NT and 2000. */
2932 if (g_PlatformId == VER_PLATFORM_WIN32_NT && advapi_lib != NULL)
2933 {
2934 p = (struct my_acl *)alloc_clear((unsigned)sizeof(struct my_acl));
2935 if (p != NULL)
2936 {
2937 if (pGetNamedSecurityInfo(
2938 (LPTSTR)fname, // Abstract filename
2939 SE_FILE_OBJECT, // File Object
2940 // Retrieve the entire security descriptor.
2941 OWNER_SECURITY_INFORMATION |
2942 GROUP_SECURITY_INFORMATION |
2943 DACL_SECURITY_INFORMATION |
2944 SACL_SECURITY_INFORMATION,
2945 &p->pSidOwner, // Ownership information.
2946 &p->pSidGroup, // Group membership.
2947 &p->pDacl, // Discretionary information.
2948 &p->pSacl, // For auditing purposes.
2949 &p->pSecurityDescriptor
2950 ) != ERROR_SUCCESS)
2951 {
2952 mch_free_acl((vim_acl_T)p);
2953 p = NULL;
2954 }
2955 }
2956 }
2957
2958 return (vim_acl_T)p;
2959#endif
2960}
2961
2962/*
2963 * Set the ACL of file "fname" to "acl" (unless it's NULL).
2964 * Errors are ignored.
2965 * This must only be called with "acl" equal to what mch_get_acl() returned.
2966 */
2967 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002968mch_set_acl(char_u *fname, vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002969{
2970#ifdef HAVE_ACL
2971 struct my_acl *p = (struct my_acl *)acl;
2972
2973 if (p != NULL && advapi_lib != NULL)
2974 (void)pSetNamedSecurityInfo(
2975 (LPTSTR)fname, // Abstract filename
2976 SE_FILE_OBJECT, // File Object
2977 // Retrieve the entire security descriptor.
2978 OWNER_SECURITY_INFORMATION |
2979 GROUP_SECURITY_INFORMATION |
2980 DACL_SECURITY_INFORMATION |
2981 SACL_SECURITY_INFORMATION,
2982 p->pSidOwner, // Ownership information.
2983 p->pSidGroup, // Group membership.
2984 p->pDacl, // Discretionary information.
2985 p->pSacl // For auditing purposes.
2986 );
2987#endif
2988}
2989
2990 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002991mch_free_acl(vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002992{
2993#ifdef HAVE_ACL
2994 struct my_acl *p = (struct my_acl *)acl;
2995
2996 if (p != NULL)
2997 {
2998 LocalFree(p->pSecurityDescriptor); // Free the memory just in case
2999 vim_free(p);
3000 }
3001#endif
3002}
3003
3004#ifndef FEAT_GUI_W32
3005
3006/*
3007 * handler for ctrl-break, ctrl-c interrupts, and fatal events.
3008 */
3009 static BOOL WINAPI
3010handler_routine(
3011 DWORD dwCtrlType)
3012{
3013 switch (dwCtrlType)
3014 {
3015 case CTRL_C_EVENT:
3016 if (ctrl_c_interrupts)
3017 g_fCtrlCPressed = TRUE;
3018 return TRUE;
3019
3020 case CTRL_BREAK_EVENT:
3021 g_fCBrkPressed = TRUE;
3022 return TRUE;
3023
3024 /* fatal events: shut down gracefully */
3025 case CTRL_CLOSE_EVENT:
3026 case CTRL_LOGOFF_EVENT:
3027 case CTRL_SHUTDOWN_EVENT:
3028 windgoto((int)Rows - 1, 0);
3029 g_fForceExit = TRUE;
3030
Bram Moolenaar0fde2902008-03-16 13:54:13 +00003031 vim_snprintf((char *)IObuff, IOSIZE, _("Vim: Caught %s event\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003032 (dwCtrlType == CTRL_CLOSE_EVENT
3033 ? _("close")
3034 : dwCtrlType == CTRL_LOGOFF_EVENT
3035 ? _("logoff")
3036 : _("shutdown")));
3037#ifdef DEBUG
3038 OutputDebugString(IObuff);
3039#endif
3040
3041 preserve_exit(); /* output IObuff, preserve files and exit */
3042
3043 return TRUE; /* not reached */
3044
3045 default:
3046 return FALSE;
3047 }
3048}
3049
3050
3051/*
3052 * set the tty in (raw) ? "raw" : "cooked" mode
3053 */
3054 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003055mch_settmode(int tmode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003056{
3057 DWORD cmodein;
3058 DWORD cmodeout;
3059 BOOL bEnableHandler;
3060
3061 GetConsoleMode(g_hConIn, &cmodein);
3062 GetConsoleMode(g_hConOut, &cmodeout);
3063 if (tmode == TMODE_RAW)
3064 {
3065 cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3066 ENABLE_ECHO_INPUT);
3067#ifdef FEAT_MOUSE
3068 if (g_fMouseActive)
3069 cmodein |= ENABLE_MOUSE_INPUT;
3070#endif
3071 cmodeout &= ~(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3072 bEnableHandler = TRUE;
3073 }
3074 else /* cooked */
3075 {
3076 cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
3077 ENABLE_ECHO_INPUT);
3078 cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
3079 bEnableHandler = FALSE;
3080 }
3081 SetConsoleMode(g_hConIn, cmodein);
3082 SetConsoleMode(g_hConOut, cmodeout);
3083 SetConsoleCtrlHandler(handler_routine, bEnableHandler);
3084
3085#ifdef MCH_WRITE_DUMP
3086 if (fdDump)
3087 {
3088 fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n",
3089 tmode == TMODE_RAW ? "raw" :
3090 tmode == TMODE_COOK ? "cooked" : "normal",
3091 cmodein, cmodeout);
3092 fflush(fdDump);
3093 }
3094#endif
3095}
3096
3097
3098/*
3099 * Get the size of the current window in `Rows' and `Columns'
3100 * Return OK when size could be determined, FAIL otherwise.
3101 */
3102 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003103mch_get_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003104{
3105 CONSOLE_SCREEN_BUFFER_INFO csbi;
3106
3107 if (!g_fTermcapMode && g_cbTermcap.IsValid)
3108 {
3109 /*
3110 * For some reason, we are trying to get the screen dimensions
3111 * even though we are not in termcap mode. The 'Rows' and 'Columns'
3112 * variables are really intended to mean the size of Vim screen
3113 * while in termcap mode.
3114 */
3115 Rows = g_cbTermcap.Info.dwSize.Y;
3116 Columns = g_cbTermcap.Info.dwSize.X;
3117 }
3118 else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3119 {
3120 Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3121 Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3122 }
3123 else
3124 {
3125 Rows = 25;
3126 Columns = 80;
3127 }
3128 return OK;
3129}
3130
3131/*
3132 * Set a console window to `xSize' * `ySize'
3133 */
3134 static void
3135ResizeConBufAndWindow(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003136 HANDLE hConsole,
3137 int xSize,
3138 int ySize)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003139{
3140 CONSOLE_SCREEN_BUFFER_INFO csbi; /* hold current console buffer info */
3141 SMALL_RECT srWindowRect; /* hold the new console size */
3142 COORD coordScreen;
3143
3144#ifdef MCH_WRITE_DUMP
3145 if (fdDump)
3146 {
3147 fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
3148 fflush(fdDump);
3149 }
3150#endif
3151
3152 /* get the largest size we can size the console window to */
3153 coordScreen = GetLargestConsoleWindowSize(hConsole);
3154
3155 /* define the new console window size and scroll position */
3156 srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
3157 srWindowRect.Right = (SHORT) (min(xSize, coordScreen.X) - 1);
3158 srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
3159
3160 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
3161 {
3162 int sx, sy;
3163
3164 sx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
3165 sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
3166 if (sy < ySize || sx < xSize)
3167 {
3168 /*
3169 * Increasing number of lines/columns, do buffer first.
3170 * Use the maximal size in x and y direction.
3171 */
3172 if (sy < ySize)
3173 coordScreen.Y = ySize;
3174 else
3175 coordScreen.Y = sy;
3176 if (sx < xSize)
3177 coordScreen.X = xSize;
3178 else
3179 coordScreen.X = sx;
3180 SetConsoleScreenBufferSize(hConsole, coordScreen);
3181 }
3182 }
3183
3184 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &srWindowRect))
3185 {
3186#ifdef MCH_WRITE_DUMP
3187 if (fdDump)
3188 {
3189 fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
3190 GetLastError());
3191 fflush(fdDump);
3192 }
3193#endif
3194 }
3195
3196 /* define the new console buffer size */
3197 coordScreen.X = xSize;
3198 coordScreen.Y = ySize;
3199
3200 if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
3201 {
3202#ifdef MCH_WRITE_DUMP
3203 if (fdDump)
3204 {
3205 fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
3206 GetLastError());
3207 fflush(fdDump);
3208 }
3209#endif
3210 }
3211}
3212
3213
3214/*
3215 * Set the console window to `Rows' * `Columns'
3216 */
3217 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003218mch_set_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003219{
3220 COORD coordScreen;
3221
3222 /* Don't change window size while still starting up */
3223 if (suppress_winsize != 0)
3224 {
3225 suppress_winsize = 2;
3226 return;
3227 }
3228
3229 if (term_console)
3230 {
3231 coordScreen = GetLargestConsoleWindowSize(g_hConOut);
3232
3233 /* Clamp Rows and Columns to reasonable values */
3234 if (Rows > coordScreen.Y)
3235 Rows = coordScreen.Y;
3236 if (Columns > coordScreen.X)
3237 Columns = coordScreen.X;
3238
3239 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
3240 }
3241}
3242
3243/*
3244 * Rows and/or Columns has changed.
3245 */
3246 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003247mch_new_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003248{
3249 set_scroll_region(0, 0, Columns - 1, Rows - 1);
3250}
3251
3252
3253/*
3254 * Called when started up, to set the winsize that was delayed.
3255 */
3256 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003257mch_set_winsize_now(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003258{
3259 if (suppress_winsize == 2)
3260 {
3261 suppress_winsize = 0;
3262 mch_set_shellsize();
3263 shell_resized();
3264 }
3265 suppress_winsize = 0;
3266}
3267#endif /* FEAT_GUI_W32 */
3268
3269
3270
3271#if defined(FEAT_GUI_W32) || defined(PROTO)
3272
3273/*
3274 * Specialised version of system() for Win32 GUI mode.
3275 * This version proceeds as follows:
3276 * 1. Create a console window for use by the subprocess
3277 * 2. Run the subprocess (it gets the allocated console by default)
3278 * 3. Wait for the subprocess to terminate and get its exit code
3279 * 4. Prompt the user to press a key to close the console window
3280 */
3281 static int
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003282mch_system_classic(char *cmd, int options)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003283{
3284 STARTUPINFO si;
3285 PROCESS_INFORMATION pi;
3286 DWORD ret = 0;
3287 HWND hwnd = GetFocus();
3288
3289 si.cb = sizeof(si);
3290 si.lpReserved = NULL;
3291 si.lpDesktop = NULL;
3292 si.lpTitle = NULL;
3293 si.dwFlags = STARTF_USESHOWWINDOW;
3294 /*
3295 * It's nicer to run a filter command in a minimized window, but in
3296 * Windows 95 this makes the command MUCH slower. We can't do it under
3297 * Win32s either as it stops the synchronous spawn workaround working.
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01003298 * Don't activate the window to keep focus on Vim.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003299 */
3300 if ((options & SHELL_DOOUT) && !mch_windows95() && !gui_is_win32s())
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01003301 si.wShowWindow = SW_SHOWMINNOACTIVE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003302 else
3303 si.wShowWindow = SW_SHOWNORMAL;
3304 si.cbReserved2 = 0;
3305 si.lpReserved2 = NULL;
3306
3307 /* There is a strange error on Windows 95 when using "c:\\command.com".
3308 * When the "c:\\" is left out it works OK...? */
3309 if (mch_windows95()
3310 && (STRNICMP(cmd, "c:/command.com", 14) == 0
3311 || STRNICMP(cmd, "c:\\command.com", 14) == 0))
3312 cmd += 3;
3313
3314 /* Now, run the command */
3315 CreateProcess(NULL, /* Executable name */
3316 cmd, /* Command to execute */
3317 NULL, /* Process security attributes */
3318 NULL, /* Thread security attributes */
3319 FALSE, /* Inherit handles */
3320 CREATE_DEFAULT_ERROR_MODE | /* Creation flags */
3321 CREATE_NEW_CONSOLE,
3322 NULL, /* Environment */
3323 NULL, /* Current directory */
3324 &si, /* Startup information */
3325 &pi); /* Process information */
3326
3327
3328 /* Wait for the command to terminate before continuing */
3329 if (g_PlatformId != VER_PLATFORM_WIN32s)
3330 {
3331#ifdef FEAT_GUI
3332 int delay = 1;
3333
3334 /* Keep updating the window while waiting for the shell to finish. */
3335 for (;;)
3336 {
3337 MSG msg;
3338
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02003339 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003340 {
3341 TranslateMessage(&msg);
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02003342 pDispatchMessage(&msg);
Bram Moolenaare4195c52012-08-02 12:31:44 +02003343 delay = 1;
3344 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003345 }
3346 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
3347 break;
3348
3349 /* We start waiting for a very short time and then increase it, so
3350 * that we respond quickly when the process is quick, and don't
3351 * consume too much overhead when it's slow. */
3352 if (delay < 50)
3353 delay += 10;
3354 }
3355#else
3356 WaitForSingleObject(pi.hProcess, INFINITE);
3357#endif
3358
3359 /* Get the command exit code */
3360 GetExitCodeProcess(pi.hProcess, &ret);
3361 }
3362 else
3363 {
3364 /*
3365 * This ugly code is the only quick way of performing
3366 * a synchronous spawn under Win32s. Yuk.
3367 */
3368 num_windows = 0;
3369 EnumWindows(win32ssynch_cb, 0);
3370 old_num_windows = num_windows;
3371 do
3372 {
3373 Sleep(1000);
3374 num_windows = 0;
3375 EnumWindows(win32ssynch_cb, 0);
3376 } while (num_windows == old_num_windows);
3377 ret = 0;
3378 }
3379
3380 /* Close the handles to the subprocess, so that it goes away */
3381 CloseHandle(pi.hThread);
3382 CloseHandle(pi.hProcess);
3383
3384 /* Try to get input focus back. Doesn't always work though. */
3385 PostMessage(hwnd, WM_SETFOCUS, 0, 0);
3386
3387 return ret;
3388}
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003389
3390/*
3391 * Thread launched by the gui to send the current buffer data to the
3392 * process. This way avoid to hang up vim totally if the children
3393 * process take a long time to process the lines.
3394 */
3395 static DWORD WINAPI
3396sub_process_writer(LPVOID param)
3397{
3398 HANDLE g_hChildStd_IN_Wr = param;
3399 linenr_T lnum = curbuf->b_op_start.lnum;
3400 DWORD len = 0;
3401 DWORD l;
3402 char_u *lp = ml_get(lnum);
3403 char_u *s;
3404 int written = 0;
3405
3406 for (;;)
3407 {
3408 l = (DWORD)STRLEN(lp + written);
3409 if (l == 0)
3410 len = 0;
3411 else if (lp[written] == NL)
3412 {
3413 /* NL -> NUL translation */
3414 WriteFile(g_hChildStd_IN_Wr, "", 1, &len, NULL);
3415 }
3416 else
3417 {
3418 s = vim_strchr(lp + written, NL);
3419 WriteFile(g_hChildStd_IN_Wr, (char *)lp + written,
3420 s == NULL ? l : (DWORD)(s - (lp + written)),
3421 &len, NULL);
3422 }
3423 if (len == (int)l)
3424 {
3425 /* Finished a line, add a NL, unless this line should not have
3426 * one. */
3427 if (lnum != curbuf->b_op_end.lnum
3428 || !curbuf->b_p_bin
3429 || (lnum != curbuf->b_no_eol_lnum
3430 && (lnum != curbuf->b_ml.ml_line_count
3431 || curbuf->b_p_eol)))
3432 {
3433 WriteFile(g_hChildStd_IN_Wr, "\n", 1, &ignored, NULL);
3434 }
3435
3436 ++lnum;
3437 if (lnum > curbuf->b_op_end.lnum)
3438 break;
3439
3440 lp = ml_get(lnum);
3441 written = 0;
3442 }
3443 else if (len > 0)
3444 written += len;
3445 }
3446
3447 /* finished all the lines, close pipe */
3448 CloseHandle(g_hChildStd_IN_Wr);
3449 ExitThread(0);
3450}
3451
3452
3453# define BUFLEN 100 /* length for buffer, stolen from unix version */
3454
3455/*
3456 * This function read from the children's stdout and write the
3457 * data on screen or in the buffer accordingly.
3458 */
3459 static void
3460dump_pipe(int options,
3461 HANDLE g_hChildStd_OUT_Rd,
3462 garray_T *ga,
3463 char_u buffer[],
3464 DWORD *buffer_off)
3465{
3466 DWORD availableBytes = 0;
3467 DWORD i;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003468 int ret;
3469 DWORD len;
3470 DWORD toRead;
3471 int repeatCount;
3472
3473 /* we query the pipe to see if there is any data to read
3474 * to avoid to perform a blocking read */
3475 ret = PeekNamedPipe(g_hChildStd_OUT_Rd, /* pipe to query */
3476 NULL, /* optional buffer */
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02003477 0, /* buffer size */
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003478 NULL, /* number of read bytes */
3479 &availableBytes, /* available bytes total */
3480 NULL); /* byteLeft */
3481
3482 repeatCount = 0;
3483 /* We got real data in the pipe, read it */
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02003484 while (ret != 0 && availableBytes > 0)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003485 {
3486 repeatCount++;
3487 toRead =
3488# ifdef FEAT_MBYTE
3489 (DWORD)(BUFLEN - *buffer_off);
3490# else
3491 (DWORD)BUFLEN;
3492# endif
3493 toRead = availableBytes < toRead ? availableBytes : toRead;
3494 ReadFile(g_hChildStd_OUT_Rd, buffer
3495# ifdef FEAT_MBYTE
3496 + *buffer_off, toRead
3497# else
3498 , toRead
3499# endif
3500 , &len, NULL);
3501
3502 /* If we haven't read anything, there is a problem */
3503 if (len == 0)
3504 break;
3505
3506 availableBytes -= len;
3507
3508 if (options & SHELL_READ)
3509 {
3510 /* Do NUL -> NL translation, append NL separated
3511 * lines to the current buffer. */
3512 for (i = 0; i < len; ++i)
3513 {
3514 if (buffer[i] == NL)
3515 append_ga_line(ga);
3516 else if (buffer[i] == NUL)
3517 ga_append(ga, NL);
3518 else
3519 ga_append(ga, buffer[i]);
3520 }
3521 }
3522# ifdef FEAT_MBYTE
3523 else if (has_mbyte)
3524 {
3525 int l;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02003526 int c;
3527 char_u *p;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003528
3529 len += *buffer_off;
3530 buffer[len] = NUL;
3531
3532 /* Check if the last character in buffer[] is
3533 * incomplete, keep these bytes for the next
3534 * round. */
3535 for (p = buffer; p < buffer + len; p += l)
3536 {
3537 l = mb_cptr2len(p);
3538 if (l == 0)
3539 l = 1; /* NUL byte? */
3540 else if (MB_BYTE2LEN(*p) != l)
3541 break;
3542 }
3543 if (p == buffer) /* no complete character */
3544 {
3545 /* avoid getting stuck at an illegal byte */
3546 if (len >= 12)
3547 ++p;
3548 else
3549 {
3550 *buffer_off = len;
3551 return;
3552 }
3553 }
3554 c = *p;
3555 *p = NUL;
3556 msg_puts(buffer);
3557 if (p < buffer + len)
3558 {
3559 *p = c;
3560 *buffer_off = (DWORD)((buffer + len) - p);
3561 mch_memmove(buffer, p, *buffer_off);
3562 return;
3563 }
3564 *buffer_off = 0;
3565 }
3566# endif /* FEAT_MBYTE */
3567 else
3568 {
3569 buffer[len] = NUL;
3570 msg_puts(buffer);
3571 }
3572
3573 windgoto(msg_row, msg_col);
3574 cursor_on();
3575 out_flush();
3576 }
3577}
3578
3579/*
3580 * Version of system to use for windows NT > 5.0 (Win2K), use pipe
3581 * for communication and doesn't open any new window.
3582 */
3583 static int
3584mch_system_piped(char *cmd, int options)
3585{
3586 STARTUPINFO si;
3587 PROCESS_INFORMATION pi;
3588 DWORD ret = 0;
3589
3590 HANDLE g_hChildStd_IN_Rd = NULL;
3591 HANDLE g_hChildStd_IN_Wr = NULL;
3592 HANDLE g_hChildStd_OUT_Rd = NULL;
3593 HANDLE g_hChildStd_OUT_Wr = NULL;
3594
3595 char_u buffer[BUFLEN + 1]; /* reading buffer + size */
3596 DWORD len;
3597
3598 /* buffer used to receive keys */
3599 char_u ta_buf[BUFLEN + 1]; /* TypeAHead */
3600 int ta_len = 0; /* valid bytes in ta_buf[] */
3601
3602 DWORD i;
3603 int c;
3604 int noread_cnt = 0;
3605 garray_T ga;
3606 int delay = 1;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003607 DWORD buffer_off = 0; /* valid bytes in buffer[] */
Bram Moolenaar6b707b42012-02-21 21:22:44 +01003608 char *p = NULL;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003609
3610 SECURITY_ATTRIBUTES saAttr;
3611
3612 /* Set the bInheritHandle flag so pipe handles are inherited. */
3613 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3614 saAttr.bInheritHandle = TRUE;
3615 saAttr.lpSecurityDescriptor = NULL;
3616
3617 if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)
3618 /* Ensure the read handle to the pipe for STDOUT is not inherited. */
3619 || ! pSetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)
3620 /* Create a pipe for the child process's STDIN. */
3621 || ! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)
3622 /* Ensure the write handle to the pipe for STDIN is not inherited. */
3623 || ! pSetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
3624 {
3625 CloseHandle(g_hChildStd_IN_Rd);
3626 CloseHandle(g_hChildStd_IN_Wr);
3627 CloseHandle(g_hChildStd_OUT_Rd);
3628 CloseHandle(g_hChildStd_OUT_Wr);
3629 MSG_PUTS(_("\nCannot create pipes\n"));
3630 }
3631
3632 si.cb = sizeof(si);
3633 si.lpReserved = NULL;
3634 si.lpDesktop = NULL;
3635 si.lpTitle = NULL;
3636 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
3637
3638 /* set-up our file redirection */
3639 si.hStdError = g_hChildStd_OUT_Wr;
3640 si.hStdOutput = g_hChildStd_OUT_Wr;
3641 si.hStdInput = g_hChildStd_IN_Rd;
3642 si.wShowWindow = SW_HIDE;
3643 si.cbReserved2 = 0;
3644 si.lpReserved2 = NULL;
3645
3646 if (options & SHELL_READ)
3647 ga_init2(&ga, 1, BUFLEN);
3648
Bram Moolenaar6b707b42012-02-21 21:22:44 +01003649 if (cmd != NULL)
3650 {
3651 p = (char *)vim_strsave((char_u *)cmd);
3652 if (p != NULL)
3653 unescape_shellxquote((char_u *)p, p_sxe);
3654 else
3655 p = cmd;
3656 }
3657
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003658 /* Now, run the command */
3659 CreateProcess(NULL, /* Executable name */
Bram Moolenaar6b707b42012-02-21 21:22:44 +01003660 p, /* Command to execute */
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003661 NULL, /* Process security attributes */
3662 NULL, /* Thread security attributes */
3663
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02003664 // this command can be litigious, handle inheritance was
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003665 // deactivated for pending temp file, but, if we deactivate
3666 // it, the pipes don't work for some reason.
3667 TRUE, /* Inherit handles, first deactivated,
3668 * but needed */
3669 CREATE_DEFAULT_ERROR_MODE, /* Creation flags */
3670 NULL, /* Environment */
3671 NULL, /* Current directory */
3672 &si, /* Startup information */
3673 &pi); /* Process information */
3674
Bram Moolenaar6b707b42012-02-21 21:22:44 +01003675 if (p != cmd)
3676 vim_free(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003677
3678 /* Close our unused side of the pipes */
3679 CloseHandle(g_hChildStd_IN_Rd);
3680 CloseHandle(g_hChildStd_OUT_Wr);
3681
3682 if (options & SHELL_WRITE)
3683 {
3684 HANDLE thread =
3685 CreateThread(NULL, /* security attributes */
3686 0, /* default stack size */
3687 sub_process_writer, /* function to be executed */
3688 g_hChildStd_IN_Wr, /* parameter */
3689 0, /* creation flag, start immediately */
3690 NULL); /* we don't care about thread id */
3691 CloseHandle(thread);
3692 g_hChildStd_IN_Wr = NULL;
3693 }
3694
3695 /* Keep updating the window while waiting for the shell to finish. */
3696 for (;;)
3697 {
3698 MSG msg;
3699
3700 if (PeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
3701 {
3702 TranslateMessage(&msg);
3703 DispatchMessage(&msg);
3704 }
3705
3706 /* write pipe information in the window */
3707 if ((options & (SHELL_READ|SHELL_WRITE))
3708# ifdef FEAT_GUI
3709 || gui.in_use
3710# endif
3711 )
3712 {
3713 len = 0;
3714 if (!(options & SHELL_EXPAND)
3715 && ((options &
3716 (SHELL_READ|SHELL_WRITE|SHELL_COOKED))
3717 != (SHELL_READ|SHELL_WRITE|SHELL_COOKED)
3718# ifdef FEAT_GUI
3719 || gui.in_use
3720# endif
3721 )
3722 && (ta_len > 0 || noread_cnt > 4))
3723 {
3724 if (ta_len == 0)
3725 {
3726 /* Get extra characters when we don't have any. Reset the
3727 * counter and timer. */
3728 noread_cnt = 0;
3729# if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
3730 gettimeofday(&start_tv, NULL);
3731# endif
3732 len = ui_inchar(ta_buf, BUFLEN, 10L, 0);
3733 }
3734 if (ta_len > 0 || len > 0)
3735 {
3736 /*
3737 * For pipes: Check for CTRL-C: send interrupt signal to
3738 * child. Check for CTRL-D: EOF, close pipe to child.
3739 */
3740 if (len == 1 && cmd != NULL)
3741 {
3742 if (ta_buf[ta_len] == Ctrl_C)
3743 {
3744 /* Learn what exit code is expected, for
3745 * now put 9 as SIGKILL */
3746 TerminateProcess(pi.hProcess, 9);
3747 }
3748 if (ta_buf[ta_len] == Ctrl_D)
3749 {
3750 CloseHandle(g_hChildStd_IN_Wr);
3751 g_hChildStd_IN_Wr = NULL;
3752 }
3753 }
3754
3755 /* replace K_BS by <BS> and K_DEL by <DEL> */
3756 for (i = ta_len; i < ta_len + len; ++i)
3757 {
3758 if (ta_buf[i] == CSI && len - i > 2)
3759 {
3760 c = TERMCAP2KEY(ta_buf[i + 1], ta_buf[i + 2]);
3761 if (c == K_DEL || c == K_KDEL || c == K_BS)
3762 {
3763 mch_memmove(ta_buf + i + 1, ta_buf + i + 3,
3764 (size_t)(len - i - 2));
3765 if (c == K_DEL || c == K_KDEL)
3766 ta_buf[i] = DEL;
3767 else
3768 ta_buf[i] = Ctrl_H;
3769 len -= 2;
3770 }
3771 }
3772 else if (ta_buf[i] == '\r')
3773 ta_buf[i] = '\n';
3774# ifdef FEAT_MBYTE
3775 if (has_mbyte)
3776 i += (*mb_ptr2len_len)(ta_buf + i,
3777 ta_len + len - i) - 1;
3778# endif
3779 }
3780
3781 /*
3782 * For pipes: echo the typed characters. For a pty this
3783 * does not seem to work.
3784 */
3785 for (i = ta_len; i < ta_len + len; ++i)
3786 {
3787 if (ta_buf[i] == '\n' || ta_buf[i] == '\b')
3788 msg_putchar(ta_buf[i]);
3789# ifdef FEAT_MBYTE
3790 else if (has_mbyte)
3791 {
3792 int l = (*mb_ptr2len)(ta_buf + i);
3793
3794 msg_outtrans_len(ta_buf + i, l);
3795 i += l - 1;
3796 }
3797# endif
3798 else
3799 msg_outtrans_len(ta_buf + i, 1);
3800 }
3801 windgoto(msg_row, msg_col);
3802 out_flush();
3803
3804 ta_len += len;
3805
3806 /*
3807 * Write the characters to the child, unless EOF has been
3808 * typed for pipes. Write one character at a time, to
3809 * avoid losing too much typeahead. When writing buffer
3810 * lines, drop the typed characters (only check for
3811 * CTRL-C).
3812 */
3813 if (options & SHELL_WRITE)
3814 ta_len = 0;
3815 else if (g_hChildStd_IN_Wr != NULL)
3816 {
3817 WriteFile(g_hChildStd_IN_Wr, (char*)ta_buf,
3818 1, &len, NULL);
3819 // if we are typing in, we want to keep things reactive
3820 delay = 1;
3821 if (len > 0)
3822 {
3823 ta_len -= len;
3824 mch_memmove(ta_buf, ta_buf + len, ta_len);
3825 }
3826 }
3827 }
3828 }
3829 }
3830
3831 if (ta_len)
3832 ui_inchar_undo(ta_buf, ta_len);
3833
3834 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
3835 {
Bram Moolenaar2eba1822011-08-27 15:10:04 +02003836 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003837 break;
3838 }
3839
3840 ++noread_cnt;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02003841 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02003842
3843 /* We start waiting for a very short time and then increase it, so
3844 * that we respond quickly when the process is quick, and don't
3845 * consume too much overhead when it's slow. */
3846 if (delay < 50)
3847 delay += 10;
3848 }
3849
3850 /* Close the pipe */
3851 CloseHandle(g_hChildStd_OUT_Rd);
3852 if (g_hChildStd_IN_Wr != NULL)
3853 CloseHandle(g_hChildStd_IN_Wr);
3854
3855 WaitForSingleObject(pi.hProcess, INFINITE);
3856
3857 /* Get the command exit code */
3858 GetExitCodeProcess(pi.hProcess, &ret);
3859
3860 if (options & SHELL_READ)
3861 {
3862 if (ga.ga_len > 0)
3863 {
3864 append_ga_line(&ga);
3865 /* remember that the NL was missing */
3866 curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
3867 }
3868 else
3869 curbuf->b_no_eol_lnum = 0;
3870 ga_clear(&ga);
3871 }
3872
3873 /* Close the handles to the subprocess, so that it goes away */
3874 CloseHandle(pi.hThread);
3875 CloseHandle(pi.hProcess);
3876
3877 return ret;
3878}
3879
3880 static int
3881mch_system(char *cmd, int options)
3882{
3883 /* if we can pipe and the shelltemp option is off */
3884 if (allowPiping && !p_stmp)
3885 return mch_system_piped(cmd, options);
3886 else
3887 return mch_system_classic(cmd, options);
3888}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003889#else
3890
3891# define mch_system(c, o) system(c)
3892
3893#endif
3894
3895/*
3896 * Either execute a command by calling the shell or start a new shell
3897 */
3898 int
3899mch_call_shell(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003900 char_u *cmd,
3901 int options) /* SHELL_*, see vim.h */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902{
3903 int x = 0;
3904 int tmode = cur_tmode;
3905#ifdef FEAT_TITLE
3906 char szShellTitle[512];
3907
3908 /* Change the title to reflect that we are in a subshell. */
3909 if (GetConsoleTitle(szShellTitle, sizeof(szShellTitle) - 4) > 0)
3910 {
3911 if (cmd == NULL)
3912 strcat(szShellTitle, " :sh");
3913 else
3914 {
3915 strcat(szShellTitle, " - !");
3916 if ((strlen(szShellTitle) + strlen(cmd) < sizeof(szShellTitle)))
3917 strcat(szShellTitle, cmd);
3918 }
3919 mch_settitle(szShellTitle, NULL);
3920 }
3921#endif
3922
3923 out_flush();
3924
3925#ifdef MCH_WRITE_DUMP
3926 if (fdDump)
3927 {
3928 fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
3929 fflush(fdDump);
3930 }
3931#endif
3932
3933 /*
3934 * Catch all deadly signals while running the external command, because a
3935 * CTRL-C, Ctrl-Break or illegal instruction might otherwise kill us.
3936 */
3937 signal(SIGINT, SIG_IGN);
3938#if defined(__GNUC__) && !defined(__MINGW32__)
3939 signal(SIGKILL, SIG_IGN);
3940#else
3941 signal(SIGBREAK, SIG_IGN);
3942#endif
3943 signal(SIGILL, SIG_IGN);
3944 signal(SIGFPE, SIG_IGN);
3945 signal(SIGSEGV, SIG_IGN);
3946 signal(SIGTERM, SIG_IGN);
3947 signal(SIGABRT, SIG_IGN);
3948
3949 if (options & SHELL_COOKED)
3950 settmode(TMODE_COOK); /* set to normal mode */
3951
3952 if (cmd == NULL)
3953 {
3954 x = mch_system(p_sh, options);
3955 }
3956 else
3957 {
3958 /* we use "command" or "cmd" to start the shell; slow but easy */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01003959 char_u *newcmd = NULL;
3960 char_u *cmdbase = cmd;
3961 long_u cmdlen;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01003962
3963 /* Skip a leading ", ( and "(. */
3964 if (*cmdbase == '"' )
3965 ++cmdbase;
3966 if (*cmdbase == '(')
3967 ++cmdbase;
3968
3969 if ((STRNICMP(cmdbase, "start", 5) == 0) && vim_iswhite(cmdbase[5]))
3970 {
3971 STARTUPINFO si;
3972 PROCESS_INFORMATION pi;
3973 DWORD flags = CREATE_NEW_CONSOLE;
3974 char_u *p;
3975
3976 si.cb = sizeof(si);
3977 si.lpReserved = NULL;
3978 si.lpDesktop = NULL;
3979 si.lpTitle = NULL;
3980 si.dwFlags = 0;
3981 si.cbReserved2 = 0;
3982 si.lpReserved2 = NULL;
3983
3984 cmdbase = skipwhite(cmdbase + 5);
3985 if ((STRNICMP(cmdbase, "/min", 4) == 0)
3986 && vim_iswhite(cmdbase[4]))
3987 {
3988 cmdbase = skipwhite(cmdbase + 4);
3989 si.dwFlags = STARTF_USESHOWWINDOW;
3990 si.wShowWindow = SW_SHOWMINNOACTIVE;
3991 }
3992 else if ((STRNICMP(cmdbase, "/b", 2) == 0)
3993 && vim_iswhite(cmdbase[2]))
3994 {
3995 cmdbase = skipwhite(cmdbase + 2);
3996 flags = CREATE_NO_WINDOW;
3997 si.dwFlags = STARTF_USESTDHANDLES;
3998 si.hStdInput = CreateFile("\\\\.\\NUL", // File name
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01003999 GENERIC_READ, // Access flags
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004000 0, // Share flags
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004001 NULL, // Security att.
4002 OPEN_EXISTING, // Open flags
4003 FILE_ATTRIBUTE_NORMAL, // File att.
4004 NULL); // Temp file
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004005 si.hStdOutput = si.hStdInput;
4006 si.hStdError = si.hStdInput;
4007 }
4008
4009 /* Remove a trailing ", ) and )" if they have a match
4010 * at the start of the command. */
4011 if (cmdbase > cmd)
4012 {
4013 p = cmdbase + STRLEN(cmdbase);
4014 if (p > cmdbase && p[-1] == '"' && *cmd == '"')
4015 *--p = NUL;
4016 if (p > cmdbase && p[-1] == ')'
4017 && (*cmd =='(' || cmd[1] == '('))
4018 *--p = NUL;
4019 }
4020
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004021 newcmd = cmdbase;
4022 unescape_shellxquote(cmdbase, p_sxe);
4023
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004024 /*
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004025 * If creating new console, arguments are passed to the
4026 * 'cmd.exe' as-is. If it's not, arguments are not treated
4027 * correctly for current 'cmd.exe'. So unescape characters in
4028 * shellxescape except '|' for avoiding to be treated as
4029 * argument to them. Pass the arguments to sub-shell.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004030 */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004031 if (flags != CREATE_NEW_CONSOLE)
4032 {
4033 char_u *subcmd;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004034 char_u *cmd_shell = mch_getenv("COMSPEC");
4035
4036 if (cmd_shell == NULL || *cmd_shell == NUL)
4037 cmd_shell = default_shell();
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004038
4039 subcmd = vim_strsave_escaped_ext(cmdbase, "|", '^', FALSE);
4040 if (subcmd != NULL)
4041 {
4042 /* make "cmd.exe /c arguments" */
4043 cmdlen = STRLEN(cmd_shell) + STRLEN(subcmd) + 5;
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004044 newcmd = lalloc(cmdlen, TRUE);
4045 if (newcmd != NULL)
4046 vim_snprintf((char *)newcmd, cmdlen, "%s /c %s",
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004047 cmd_shell, subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004048 else
4049 newcmd = cmdbase;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01004050 vim_free(subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004051 }
4052 }
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004053
4054 /*
4055 * Now, start the command as a process, so that it doesn't
4056 * inherit our handles which causes unpleasant dangling swap
4057 * files if we exit before the spawned process
4058 */
4059 if (CreateProcess(NULL, // Executable name
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004060 newcmd, // Command to execute
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004061 NULL, // Process security attributes
4062 NULL, // Thread security attributes
4063 FALSE, // Inherit handles
4064 flags, // Creation flags
4065 NULL, // Environment
4066 NULL, // Current directory
4067 &si, // Startup information
4068 &pi)) // Process information
4069 x = 0;
4070 else
4071 {
4072 x = -1;
4073#ifdef FEAT_GUI_W32
4074 EMSG(_("E371: Command not found"));
4075#endif
4076 }
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004077
4078 if (newcmd != cmdbase)
4079 vim_free(newcmd);
4080
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004081 if (si.hStdInput != NULL)
4082 {
4083 /* Close the handle to \\.\NUL */
4084 CloseHandle(si.hStdInput);
4085 }
4086 /* Close the handles to the subprocess, so that it goes away */
4087 CloseHandle(pi.hThread);
4088 CloseHandle(pi.hProcess);
4089 }
4090 else
4091 {
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01004092 cmdlen = (
Bram Moolenaar071d4272004-06-13 20:20:40 +00004093#ifdef FEAT_GUI_W32
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004094 (allowPiping && !p_stmp ? 0 : STRLEN(vimrun_path)) +
Bram Moolenaar071d4272004-06-13 20:20:40 +00004095#endif
Bram Moolenaar0fde2902008-03-16 13:54:13 +00004096 STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10);
4097
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004098 newcmd = lalloc(cmdlen, TRUE);
4099 if (newcmd != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004100 {
4101#if defined(FEAT_GUI_W32)
4102 if (need_vimrun_warning)
4103 {
4104 MessageBox(NULL,
4105 _("VIMRUN.EXE not found in your $PATH.\n"
4106 "External commands will not pause after completion.\n"
4107 "See :help win32-vimrun for more information."),
4108 _("Vim Warning"),
4109 MB_ICONWARNING);
4110 need_vimrun_warning = FALSE;
4111 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004112 if (!s_dont_use_vimrun && (!allowPiping || p_stmp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004113 /* Use vimrun to execute the command. It opens a console
4114 * window, which can be closed without killing Vim. */
Bram Moolenaarcc448b32010-07-14 16:52:17 +02004115 vim_snprintf((char *)newcmd, cmdlen, "%s%s%s %s %s",
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116 vimrun_path,
4117 (msg_silent != 0 || (options & SHELL_DOOUT))
4118 ? "-s " : "",
4119 p_sh, p_shcf, cmd);
4120 else
4121#endif
Bram Moolenaarcc448b32010-07-14 16:52:17 +02004122 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s",
Bram Moolenaar0fde2902008-03-16 13:54:13 +00004123 p_sh, p_shcf, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004124 x = mch_system((char *)newcmd, options);
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004125 vim_free(newcmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004127 }
4128 }
4129
4130 if (tmode == TMODE_RAW)
4131 settmode(TMODE_RAW); /* set to raw mode */
4132
4133 /* Print the return value, unless "vimrun" was used. */
4134 if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent
4135#if defined(FEAT_GUI_W32)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004136 && ((options & SHELL_DOOUT) || s_dont_use_vimrun
4137 || (allowPiping && !p_stmp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138#endif
4139 )
4140 {
4141 smsg(_("shell returned %d"), x);
4142 msg_putchar('\n');
4143 }
4144#ifdef FEAT_TITLE
4145 resettitle();
4146#endif
4147
4148 signal(SIGINT, SIG_DFL);
4149#if defined(__GNUC__) && !defined(__MINGW32__)
4150 signal(SIGKILL, SIG_DFL);
4151#else
4152 signal(SIGBREAK, SIG_DFL);
4153#endif
4154 signal(SIGILL, SIG_DFL);
4155 signal(SIGFPE, SIG_DFL);
4156 signal(SIGSEGV, SIG_DFL);
4157 signal(SIGTERM, SIG_DFL);
4158 signal(SIGABRT, SIG_DFL);
4159
4160 return x;
4161}
4162
4163
4164#ifndef FEAT_GUI_W32
4165
4166/*
4167 * Start termcap mode
4168 */
4169 static void
4170termcap_mode_start(void)
4171{
4172 DWORD cmodein;
4173
4174 if (g_fTermcapMode)
4175 return;
4176
4177 SaveConsoleBuffer(&g_cbNonTermcap);
4178
4179 if (g_cbTermcap.IsValid)
4180 {
4181 /*
4182 * We've been in termcap mode before. Restore certain screen
4183 * characteristics, including the buffer size and the window
4184 * size. Since we will be redrawing the screen, we don't need
4185 * to restore the actual contents of the buffer.
4186 */
4187 RestoreConsoleBuffer(&g_cbTermcap, FALSE);
4188 SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow);
4189 Rows = g_cbTermcap.Info.dwSize.Y;
4190 Columns = g_cbTermcap.Info.dwSize.X;
4191 }
4192 else
4193 {
4194 /*
4195 * This is our first time entering termcap mode. Clear the console
4196 * screen buffer, and resize the buffer to match the current window
4197 * size. We will use this as the size of our editing environment.
4198 */
4199 ClearConsoleBuffer(g_attrCurrent);
4200 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
4201 }
4202
4203#ifdef FEAT_TITLE
4204 resettitle();
4205#endif
4206
4207 GetConsoleMode(g_hConIn, &cmodein);
4208#ifdef FEAT_MOUSE
4209 if (g_fMouseActive)
4210 cmodein |= ENABLE_MOUSE_INPUT;
4211 else
4212 cmodein &= ~ENABLE_MOUSE_INPUT;
4213#endif
4214 cmodein |= ENABLE_WINDOW_INPUT;
4215 SetConsoleMode(g_hConIn, cmodein);
4216
4217 redraw_later_clear();
4218 g_fTermcapMode = TRUE;
4219}
4220
4221
4222/*
4223 * End termcap mode
4224 */
4225 static void
4226termcap_mode_end(void)
4227{
4228 DWORD cmodein;
4229 ConsoleBuffer *cb;
4230 COORD coord;
4231 DWORD dwDummy;
4232
4233 if (!g_fTermcapMode)
4234 return;
4235
4236 SaveConsoleBuffer(&g_cbTermcap);
4237
4238 GetConsoleMode(g_hConIn, &cmodein);
4239 cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
4240 SetConsoleMode(g_hConIn, cmodein);
4241
4242#ifdef FEAT_RESTORE_ORIG_SCREEN
4243 cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
4244#else
4245 cb = &g_cbNonTermcap;
4246#endif
4247 RestoreConsoleBuffer(cb, p_rs);
4248 SetConsoleCursorInfo(g_hConOut, &g_cci);
4249
4250 if (p_rs || exiting)
4251 {
4252 /*
4253 * Clear anything that happens to be on the current line.
4254 */
4255 coord.X = 0;
4256 coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1));
4257 FillConsoleOutputCharacter(g_hConOut, ' ',
4258 cb->Info.dwSize.X, coord, &dwDummy);
4259 /*
4260 * The following is just for aesthetics. If we are exiting without
4261 * restoring the screen, then we want to have a prompt string
4262 * appear at the bottom line. However, the command interpreter
4263 * seems to always advance the cursor one line before displaying
4264 * the prompt string, which causes the screen to scroll. To
4265 * counter this, move the cursor up one line before exiting.
4266 */
4267 if (exiting && !p_rs)
4268 coord.Y--;
4269 /*
4270 * Position the cursor at the leftmost column of the desired row.
4271 */
4272 SetConsoleCursorPosition(g_hConOut, coord);
4273 }
4274
4275 g_fTermcapMode = FALSE;
4276}
4277#endif /* FEAT_GUI_W32 */
4278
4279
4280#ifdef FEAT_GUI_W32
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00004281/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00004282 void
4283mch_write(
4284 char_u *s,
4285 int len)
4286{
4287 /* never used */
4288}
4289
4290#else
4291
4292/*
4293 * clear `n' chars, starting from `coord'
4294 */
4295 static void
4296clear_chars(
4297 COORD coord,
4298 DWORD n)
4299{
4300 DWORD dwDummy;
4301
4302 FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
4303 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy);
4304}
4305
4306
4307/*
4308 * Clear the screen
4309 */
4310 static void
4311clear_screen(void)
4312{
4313 g_coord.X = g_coord.Y = 0;
4314 clear_chars(g_coord, Rows * Columns);
4315}
4316
4317
4318/*
4319 * Clear to end of display
4320 */
4321 static void
4322clear_to_end_of_display(void)
4323{
4324 clear_chars(g_coord, (Rows - g_coord.Y - 1)
4325 * Columns + (Columns - g_coord.X));
4326}
4327
4328
4329/*
4330 * Clear to end of line
4331 */
4332 static void
4333clear_to_end_of_line(void)
4334{
4335 clear_chars(g_coord, Columns - g_coord.X);
4336}
4337
4338
4339/*
4340 * Scroll the scroll region up by `cLines' lines
4341 */
4342 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004343scroll(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004344{
4345 COORD oldcoord = g_coord;
4346
4347 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
4348 delete_lines(cLines);
4349
4350 g_coord = oldcoord;
4351}
4352
4353
4354/*
4355 * Set the scroll region
4356 */
4357 static void
4358set_scroll_region(
4359 unsigned left,
4360 unsigned top,
4361 unsigned right,
4362 unsigned bottom)
4363{
4364 if (left >= right
4365 || top >= bottom
4366 || right > (unsigned) Columns - 1
4367 || bottom > (unsigned) Rows - 1)
4368 return;
4369
4370 g_srScrollRegion.Left = left;
4371 g_srScrollRegion.Top = top;
4372 g_srScrollRegion.Right = right;
4373 g_srScrollRegion.Bottom = bottom;
4374}
4375
4376
4377/*
4378 * Insert `cLines' lines at the current cursor position
4379 */
4380 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004381insert_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004382{
4383 SMALL_RECT source;
4384 COORD dest;
4385 CHAR_INFO fill;
4386
4387 dest.X = 0;
4388 dest.Y = g_coord.Y + cLines;
4389
4390 source.Left = 0;
4391 source.Top = g_coord.Y;
4392 source.Right = g_srScrollRegion.Right;
4393 source.Bottom = g_srScrollRegion.Bottom - cLines;
4394
4395 fill.Char.AsciiChar = ' ';
4396 fill.Attributes = g_attrCurrent;
4397
4398 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
4399
4400 /* Here we have to deal with a win32 console flake: If the scroll
4401 * region looks like abc and we scroll c to a and fill with d we get
4402 * cbd... if we scroll block c one line at a time to a, we get cdd...
4403 * vim expects cdd consistently... So we have to deal with that
4404 * here... (this also occurs scrolling the same way in the other
4405 * direction). */
4406
4407 if (source.Bottom < dest.Y)
4408 {
4409 COORD coord;
4410
4411 coord.X = 0;
4412 coord.Y = source.Bottom;
4413 clear_chars(coord, Columns * (dest.Y - source.Bottom));
4414 }
4415}
4416
4417
4418/*
4419 * Delete `cLines' lines at the current cursor position
4420 */
4421 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004422delete_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004423{
4424 SMALL_RECT source;
4425 COORD dest;
4426 CHAR_INFO fill;
4427 int nb;
4428
4429 dest.X = 0;
4430 dest.Y = g_coord.Y;
4431
4432 source.Left = 0;
4433 source.Top = g_coord.Y + cLines;
4434 source.Right = g_srScrollRegion.Right;
4435 source.Bottom = g_srScrollRegion.Bottom;
4436
4437 fill.Char.AsciiChar = ' ';
4438 fill.Attributes = g_attrCurrent;
4439
4440 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
4441
4442 /* Here we have to deal with a win32 console flake: If the scroll
4443 * region looks like abc and we scroll c to a and fill with d we get
4444 * cbd... if we scroll block c one line at a time to a, we get cdd...
4445 * vim expects cdd consistently... So we have to deal with that
4446 * here... (this also occurs scrolling the same way in the other
4447 * direction). */
4448
4449 nb = dest.Y + (source.Bottom - source.Top) + 1;
4450
4451 if (nb < source.Top)
4452 {
4453 COORD coord;
4454
4455 coord.X = 0;
4456 coord.Y = nb;
4457 clear_chars(coord, Columns * (source.Top - nb));
4458 }
4459}
4460
4461
4462/*
4463 * Set the cursor position
4464 */
4465 static void
4466gotoxy(
4467 unsigned x,
4468 unsigned y)
4469{
4470 if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows)
4471 return;
4472
4473 /* external cursor coords are 1-based; internal are 0-based */
4474 g_coord.X = x - 1;
4475 g_coord.Y = y - 1;
4476 SetConsoleCursorPosition(g_hConOut, g_coord);
4477}
4478
4479
4480/*
4481 * Set the current text attribute = (foreground | background)
4482 * See ../doc/os_win32.txt for the numbers.
4483 */
4484 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004485textattr(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004486{
4487 g_attrCurrent = wAttr;
4488
4489 SetConsoleTextAttribute(g_hConOut, wAttr);
4490}
4491
4492
4493 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004494textcolor(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004495{
4496 g_attrCurrent = (g_attrCurrent & 0xf0) + wAttr;
4497
4498 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
4499}
4500
4501
4502 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004503textbackground(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504{
4505 g_attrCurrent = (g_attrCurrent & 0x0f) + (wAttr << 4);
4506
4507 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
4508}
4509
4510
4511/*
4512 * restore the default text attribute (whatever we started with)
4513 */
4514 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004515normvideo(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004516{
4517 textattr(g_attrDefault);
4518}
4519
4520
4521static WORD g_attrPreStandout = 0;
4522
4523/*
4524 * Make the text standout, by brightening it
4525 */
4526 static void
4527standout(void)
4528{
4529 g_attrPreStandout = g_attrCurrent;
4530 textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
4531}
4532
4533
4534/*
4535 * Turn off standout mode
4536 */
4537 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004538standend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004539{
4540 if (g_attrPreStandout)
4541 {
4542 textattr(g_attrPreStandout);
4543 g_attrPreStandout = 0;
4544 }
4545}
4546
4547
4548/*
Bram Moolenaarff1d0d42007-05-10 17:24:16 +00004549 * Set normal fg/bg color, based on T_ME. Called when t_me has been set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004550 */
4551 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004552mch_set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553{
4554 char_u *p;
4555 int n;
4556
4557 cterm_normal_fg_color = (g_attrDefault & 0xf) + 1;
4558 cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1;
4559 if (T_ME[0] == ESC && T_ME[1] == '|')
4560 {
4561 p = T_ME + 2;
4562 n = getdigits(&p);
4563 if (*p == 'm' && n > 0)
4564 {
4565 cterm_normal_fg_color = (n & 0xf) + 1;
4566 cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
4567 }
4568 }
4569}
4570
4571
4572/*
4573 * visual bell: flash the screen
4574 */
4575 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004576visual_bell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004577{
4578 COORD coordOrigin = {0, 0};
4579 WORD attrFlash = ~g_attrCurrent & 0xff;
4580
4581 DWORD dwDummy;
4582 LPWORD oldattrs = (LPWORD)alloc(Rows * Columns * sizeof(WORD));
4583
4584 if (oldattrs == NULL)
4585 return;
4586 ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
4587 coordOrigin, &dwDummy);
4588 FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns,
4589 coordOrigin, &dwDummy);
4590
4591 Sleep(15); /* wait for 15 msec */
4592 WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
4593 coordOrigin, &dwDummy);
4594 vim_free(oldattrs);
4595}
4596
4597
4598/*
4599 * Make the cursor visible or invisible
4600 */
4601 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004602cursor_visible(BOOL fVisible)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004603{
4604 s_cursor_visible = fVisible;
4605#ifdef MCH_CURSOR_SHAPE
4606 mch_update_cursor();
4607#endif
4608}
4609
4610
4611/*
4612 * write `cchToWrite' characters in `pchBuf' to the screen
4613 * Returns the number of characters actually written (at least one).
4614 */
4615 static BOOL
4616write_chars(
4617 LPCSTR pchBuf,
4618 DWORD cchToWrite)
4619{
4620 COORD coord = g_coord;
4621 DWORD written;
4622
4623 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cchToWrite,
4624 coord, &written);
4625 /* When writing fails or didn't write a single character, pretend one
4626 * character was written, otherwise we get stuck. */
4627 if (WriteConsoleOutputCharacter(g_hConOut, pchBuf, cchToWrite,
4628 coord, &written) == 0
4629 || written == 0)
4630 written = 1;
4631
4632 g_coord.X += (SHORT) written;
4633
4634 while (g_coord.X > g_srScrollRegion.Right)
4635 {
4636 g_coord.X -= (SHORT) Columns;
4637 if (g_coord.Y < g_srScrollRegion.Bottom)
4638 g_coord.Y++;
4639 }
4640
4641 gotoxy(g_coord.X + 1, g_coord.Y + 1);
4642
4643 return written;
4644}
4645
4646
4647/*
4648 * mch_write(): write the output buffer to the screen, translating ESC
4649 * sequences into calls to console output routines.
4650 */
4651 void
4652mch_write(
4653 char_u *s,
4654 int len)
4655{
4656 s[len] = NUL;
4657
4658 if (!term_console)
4659 {
4660 write(1, s, (unsigned)len);
4661 return;
4662 }
4663
4664 /* translate ESC | sequences into faked bios calls */
4665 while (len--)
4666 {
4667 /* optimization: use one single write_chars for runs of text,
4668 * rather than once per character It ain't curses, but it helps. */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004669 DWORD prefix = (DWORD)strcspn(s, "\n\r\b\a\033");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004670
4671 if (p_wd)
4672 {
4673 WaitForChar(p_wd);
4674 if (prefix != 0)
4675 prefix = 1;
4676 }
4677
4678 if (prefix != 0)
4679 {
4680 DWORD nWritten;
4681
4682 nWritten = write_chars(s, prefix);
4683#ifdef MCH_WRITE_DUMP
4684 if (fdDump)
4685 {
4686 fputc('>', fdDump);
4687 fwrite(s, sizeof(char_u), nWritten, fdDump);
4688 fputs("<\n", fdDump);
4689 }
4690#endif
4691 len -= (nWritten - 1);
4692 s += nWritten;
4693 }
4694 else if (s[0] == '\n')
4695 {
4696 /* \n, newline: go to the beginning of the next line or scroll */
4697 if (g_coord.Y == g_srScrollRegion.Bottom)
4698 {
4699 scroll(1);
4700 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
4701 }
4702 else
4703 {
4704 gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
4705 }
4706#ifdef MCH_WRITE_DUMP
4707 if (fdDump)
4708 fputs("\\n\n", fdDump);
4709#endif
4710 s++;
4711 }
4712 else if (s[0] == '\r')
4713 {
4714 /* \r, carriage return: go to beginning of line */
4715 gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
4716#ifdef MCH_WRITE_DUMP
4717 if (fdDump)
4718 fputs("\\r\n", fdDump);
4719#endif
4720 s++;
4721 }
4722 else if (s[0] == '\b')
4723 {
4724 /* \b, backspace: move cursor one position left */
4725 if (g_coord.X > g_srScrollRegion.Left)
4726 g_coord.X--;
4727 else if (g_coord.Y > g_srScrollRegion.Top)
4728 {
4729 g_coord.X = g_srScrollRegion.Right;
4730 g_coord.Y--;
4731 }
4732 gotoxy(g_coord.X + 1, g_coord.Y + 1);
4733#ifdef MCH_WRITE_DUMP
4734 if (fdDump)
4735 fputs("\\b\n", fdDump);
4736#endif
4737 s++;
4738 }
4739 else if (s[0] == '\a')
4740 {
4741 /* \a, bell */
4742 MessageBeep(0xFFFFFFFF);
4743#ifdef MCH_WRITE_DUMP
4744 if (fdDump)
4745 fputs("\\a\n", fdDump);
4746#endif
4747 s++;
4748 }
4749 else if (s[0] == ESC && len >= 3-1 && s[1] == '|')
4750 {
4751#ifdef MCH_WRITE_DUMP
Bram Moolenaarc0197e22004-09-13 20:26:32 +00004752 char_u *old_s = s;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004753#endif
Bram Moolenaarc0197e22004-09-13 20:26:32 +00004754 char_u *p;
4755 int arg1 = 0, arg2 = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004756
4757 switch (s[2])
4758 {
4759 /* one or two numeric arguments, separated by ';' */
4760
4761 case '0': case '1': case '2': case '3': case '4':
4762 case '5': case '6': case '7': case '8': case '9':
4763 p = s + 2;
4764 arg1 = getdigits(&p); /* no check for length! */
4765 if (p > s + len)
4766 break;
4767
4768 if (*p == ';')
4769 {
4770 ++p;
4771 arg2 = getdigits(&p); /* no check for length! */
4772 if (p > s + len)
4773 break;
4774
4775 if (*p == 'H')
4776 gotoxy(arg2, arg1);
4777 else if (*p == 'r')
4778 set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
4779 }
4780 else if (*p == 'A')
4781 {
4782 /* move cursor up arg1 lines in same column */
4783 gotoxy(g_coord.X + 1,
4784 max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
4785 }
4786 else if (*p == 'C')
4787 {
4788 /* move cursor right arg1 columns in same line */
4789 gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
4790 g_coord.Y + 1);
4791 }
4792 else if (*p == 'H')
4793 {
4794 gotoxy(1, arg1);
4795 }
4796 else if (*p == 'L')
4797 {
4798 insert_lines(arg1);
4799 }
4800 else if (*p == 'm')
4801 {
4802 if (arg1 == 0)
4803 normvideo();
4804 else
4805 textattr((WORD) arg1);
4806 }
4807 else if (*p == 'f')
4808 {
4809 textcolor((WORD) arg1);
4810 }
4811 else if (*p == 'b')
4812 {
4813 textbackground((WORD) arg1);
4814 }
4815 else if (*p == 'M')
4816 {
4817 delete_lines(arg1);
4818 }
4819
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004820 len -= (int)(p - s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004821 s = p + 1;
4822 break;
4823
4824
4825 /* Three-character escape sequences */
4826
4827 case 'A':
4828 /* move cursor up one line in same column */
4829 gotoxy(g_coord.X + 1,
4830 max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
4831 goto got3;
4832
4833 case 'B':
4834 visual_bell();
4835 goto got3;
4836
4837 case 'C':
4838 /* move cursor right one column in same line */
4839 gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
4840 g_coord.Y + 1);
4841 goto got3;
4842
4843 case 'E':
4844 termcap_mode_end();
4845 goto got3;
4846
4847 case 'F':
4848 standout();
4849 goto got3;
4850
4851 case 'f':
4852 standend();
4853 goto got3;
4854
4855 case 'H':
4856 gotoxy(1, 1);
4857 goto got3;
4858
4859 case 'j':
4860 clear_to_end_of_display();
4861 goto got3;
4862
4863 case 'J':
4864 clear_screen();
4865 goto got3;
4866
4867 case 'K':
4868 clear_to_end_of_line();
4869 goto got3;
4870
4871 case 'L':
4872 insert_lines(1);
4873 goto got3;
4874
4875 case 'M':
4876 delete_lines(1);
4877 goto got3;
4878
4879 case 'S':
4880 termcap_mode_start();
4881 goto got3;
4882
4883 case 'V':
4884 cursor_visible(TRUE);
4885 goto got3;
4886
4887 case 'v':
4888 cursor_visible(FALSE);
4889 goto got3;
4890
4891 got3:
4892 s += 3;
4893 len -= 2;
4894 }
4895
4896#ifdef MCH_WRITE_DUMP
4897 if (fdDump)
4898 {
4899 fputs("ESC | ", fdDump);
4900 fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
4901 fputc('\n', fdDump);
4902 }
4903#endif
4904 }
4905 else
4906 {
4907 /* Write a single character */
4908 DWORD nWritten;
4909
4910 nWritten = write_chars(s, 1);
4911#ifdef MCH_WRITE_DUMP
4912 if (fdDump)
4913 {
4914 fputc('>', fdDump);
4915 fwrite(s, sizeof(char_u), nWritten, fdDump);
4916 fputs("<\n", fdDump);
4917 }
4918#endif
4919
4920 len -= (nWritten - 1);
4921 s += nWritten;
4922 }
4923 }
4924
4925#ifdef MCH_WRITE_DUMP
4926 if (fdDump)
4927 fflush(fdDump);
4928#endif
4929}
4930
4931#endif /* FEAT_GUI_W32 */
4932
4933
4934/*
4935 * Delay for half a second.
4936 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00004937/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00004938 void
4939mch_delay(
4940 long msec,
4941 int ignoreinput)
4942{
4943#ifdef FEAT_GUI_W32
4944 Sleep((int)msec); /* never wait for input */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00004945#else /* Console */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004946 if (ignoreinput)
Bram Moolenaar325b7a22004-07-05 15:58:32 +00004947# ifdef FEAT_MZSCHEME
4948 if (mzthreads_allowed() && p_mzq > 0 && msec > p_mzq)
4949 {
4950 int towait = p_mzq;
4951
4952 /* if msec is large enough, wait by portions in p_mzq */
4953 while (msec > 0)
4954 {
4955 mzvim_check_threads();
4956 if (msec < towait)
4957 towait = msec;
4958 Sleep(towait);
4959 msec -= towait;
4960 }
4961 }
4962 else
4963# endif
4964 Sleep((int)msec);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004965 else
4966 WaitForChar(msec);
4967#endif
4968}
4969
4970
4971/*
4972 * this version of remove is not scared by a readonly (backup) file
4973 * Return 0 for success, -1 for failure.
4974 */
4975 int
4976mch_remove(char_u *name)
4977{
4978#ifdef FEAT_MBYTE
4979 WCHAR *wn = NULL;
4980 int n;
4981
4982 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4983 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00004984 wn = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004985 if (wn != NULL)
4986 {
4987 SetFileAttributesW(wn, FILE_ATTRIBUTE_NORMAL);
4988 n = DeleteFileW(wn) ? 0 : -1;
4989 vim_free(wn);
4990 if (n == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4991 return n;
4992 /* Retry with non-wide function (for Windows 98). */
4993 }
4994 }
4995#endif
4996 SetFileAttributes(name, FILE_ATTRIBUTE_NORMAL);
4997 return DeleteFile(name) ? 0 : -1;
4998}
4999
5000
5001/*
5002 * check for an "interrupt signal": CTRL-break or CTRL-C
5003 */
5004 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005005mch_breakcheck(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005006{
5007#ifndef FEAT_GUI_W32 /* never used */
5008 if (g_fCtrlCPressed || g_fCBrkPressed)
5009 {
5010 g_fCtrlCPressed = g_fCBrkPressed = FALSE;
5011 got_int = TRUE;
5012 }
5013#endif
5014}
5015
5016
5017/*
Bram Moolenaar11b73d62012-06-29 15:51:30 +02005018 * How much memory is available in Kbyte?
Bram Moolenaar071d4272004-06-13 20:20:40 +00005019 * Return sum of available physical and page file memory.
5020 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00005021/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00005022 long_u
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005023mch_avail_mem(int special)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005024{
Bram Moolenaar1a0cee52012-07-19 11:37:26 +02005025#ifdef MEMORYSTATUSEX
5026 PlatformId();
5027 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
5028 {
5029 MEMORYSTATUSEX ms;
5030
5031 /* Need to use GlobalMemoryStatusEx() when there is more memory than
5032 * what fits in 32 bits. But it's not always available. */
5033 ms.dwLength = sizeof(MEMORYSTATUSEX);
5034 GlobalMemoryStatusEx(&ms);
5035 return (long_u)((ms.ullAvailPhys + ms.ullAvailPageFile) >> 10);
5036 }
5037 else
5038#endif
Bram Moolenaar11b73d62012-06-29 15:51:30 +02005039 {
5040 MEMORYSTATUS ms;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005041
Bram Moolenaar11b73d62012-06-29 15:51:30 +02005042 ms.dwLength = sizeof(MEMORYSTATUS);
5043 GlobalMemoryStatus(&ms);
5044 return (long_u)((ms.dwAvailPhys + ms.dwAvailPageFile) >> 10);
5045 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005046}
5047
5048#ifdef FEAT_MBYTE
5049/*
5050 * Same code as below, but with wide functions and no comments.
5051 * Return 0 for success, non-zero for failure.
5052 */
5053 int
5054mch_wrename(WCHAR *wold, WCHAR *wnew)
5055{
5056 WCHAR *p;
5057 int i;
5058 WCHAR szTempFile[_MAX_PATH + 1];
5059 WCHAR szNewPath[_MAX_PATH + 1];
5060 HANDLE hf;
5061
5062 if (!mch_windows95())
5063 {
5064 p = wold;
5065 for (i = 0; wold[i] != NUL; ++i)
5066 if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':')
5067 && wold[i + 1] != 0)
5068 p = wold + i + 1;
5069 if ((int)(wold + i - p) < 8 || p[6] != '~')
5070 return (MoveFileW(wold, wnew) == 0);
5071 }
5072
5073 if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL)
5074 return -1;
5075 *p = NUL;
5076
5077 if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0)
5078 return -2;
5079
5080 if (!DeleteFileW(szTempFile))
5081 return -3;
5082
5083 if (!MoveFileW(wold, szTempFile))
5084 return -4;
5085
5086 if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW,
5087 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
5088 return -5;
5089 if (!CloseHandle(hf))
5090 return -6;
5091
5092 if (!MoveFileW(szTempFile, wnew))
5093 {
5094 (void)MoveFileW(szTempFile, wold);
5095 return -7;
5096 }
5097
5098 DeleteFileW(szTempFile);
5099
5100 if (!DeleteFileW(wold))
5101 return -8;
5102
5103 return 0;
5104}
5105#endif
5106
5107
5108/*
5109 * mch_rename() works around a bug in rename (aka MoveFile) in
5110 * Windows 95: rename("foo.bar", "foo.bar~") will generate a
5111 * file whose short file name is "FOO.BAR" (its long file name will
5112 * be correct: "foo.bar~"). Because a file can be accessed by
5113 * either its SFN or its LFN, "foo.bar" has effectively been
5114 * renamed to "foo.bar", which is not at all what was wanted. This
5115 * seems to happen only when renaming files with three-character
5116 * extensions by appending a suffix that does not include ".".
5117 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
5118 *
5119 * There is another problem, which isn't really a bug but isn't right either:
5120 * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be
5121 * "abcdef~1.txt" again. This has been reported on Windows NT 4.0 with
5122 * service pack 6. Doesn't seem to happen on Windows 98.
5123 *
5124 * Like rename(), returns 0 upon success, non-zero upon failure.
5125 * Should probably set errno appropriately when errors occur.
5126 */
5127 int
5128mch_rename(
5129 const char *pszOldFile,
5130 const char *pszNewFile)
5131{
5132 char szTempFile[_MAX_PATH+1];
5133 char szNewPath[_MAX_PATH+1];
5134 char *pszFilePart;
5135 HANDLE hf;
5136#ifdef FEAT_MBYTE
5137 WCHAR *wold = NULL;
5138 WCHAR *wnew = NULL;
5139 int retval = -1;
5140
5141 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
5142 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005143 wold = enc_to_utf16((char_u *)pszOldFile, NULL);
5144 wnew = enc_to_utf16((char_u *)pszNewFile, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005145 if (wold != NULL && wnew != NULL)
5146 retval = mch_wrename(wold, wnew);
5147 vim_free(wold);
5148 vim_free(wnew);
5149 if (retval == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
5150 return retval;
5151 /* Retry with non-wide function (for Windows 98). */
5152 }
5153#endif
5154
5155 /*
5156 * No need to play tricks if not running Windows 95, unless the file name
5157 * contains a "~" as the seventh character.
5158 */
5159 if (!mch_windows95())
5160 {
5161 pszFilePart = (char *)gettail((char_u *)pszOldFile);
5162 if (STRLEN(pszFilePart) < 8 || pszFilePart[6] != '~')
5163 return rename(pszOldFile, pszNewFile);
5164 }
5165
5166 /* Get base path of new file name. Undocumented feature: If pszNewFile is
5167 * a directory, no error is returned and pszFilePart will be NULL. */
5168 if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0
5169 || pszFilePart == NULL)
5170 return -1;
5171 *pszFilePart = NUL;
5172
5173 /* Get (and create) a unique temporary file name in directory of new file */
5174 if (GetTempFileName(szNewPath, "VIM", 0, szTempFile) == 0)
5175 return -2;
5176
5177 /* blow the temp file away */
5178 if (!DeleteFile(szTempFile))
5179 return -3;
5180
5181 /* rename old file to the temp file */
5182 if (!MoveFile(pszOldFile, szTempFile))
5183 return -4;
5184
5185 /* now create an empty file called pszOldFile; this prevents the operating
5186 * system using pszOldFile as an alias (SFN) if we're renaming within the
5187 * same directory. For example, we're editing a file called
5188 * filename.asc.txt by its SFN, filena~1.txt. If we rename filena~1.txt
5189 * to filena~1.txt~ (i.e., we're making a backup while writing it), the
5190 * SFN for filena~1.txt~ will be filena~1.txt, by default, which will
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005191 * cause all sorts of problems later in buf_write(). So, we create an
5192 * empty file called filena~1.txt and the system will have to find some
5193 * other SFN for filena~1.txt~, such as filena~2.txt
Bram Moolenaar071d4272004-06-13 20:20:40 +00005194 */
5195 if ((hf = CreateFile(pszOldFile, GENERIC_WRITE, 0, NULL, CREATE_NEW,
5196 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
5197 return -5;
5198 if (!CloseHandle(hf))
5199 return -6;
5200
5201 /* rename the temp file to the new file */
5202 if (!MoveFile(szTempFile, pszNewFile))
5203 {
5204 /* Renaming failed. Rename the file back to its old name, so that it
5205 * looks like nothing happened. */
5206 (void)MoveFile(szTempFile, pszOldFile);
5207
5208 return -7;
5209 }
5210
5211 /* Seems to be left around on Novell filesystems */
5212 DeleteFile(szTempFile);
5213
5214 /* finally, remove the empty old file */
5215 if (!DeleteFile(pszOldFile))
5216 return -8;
5217
5218 return 0; /* success */
5219}
5220
5221/*
5222 * Get the default shell for the current hardware platform
5223 */
5224 char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005225default_shell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005226{
5227 char* psz = NULL;
5228
5229 PlatformId();
5230
5231 if (g_PlatformId == VER_PLATFORM_WIN32_NT) /* Windows NT */
5232 psz = "cmd.exe";
5233 else if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS) /* Windows 95 */
5234 psz = "command.com";
5235
5236 return psz;
5237}
5238
5239/*
5240 * mch_access() extends access() to do more detailed check on network drives.
5241 * Returns 0 if file "n" has access rights according to "p", -1 otherwise.
5242 */
5243 int
5244mch_access(char *n, int p)
5245{
5246 HANDLE hFile;
5247 DWORD am;
5248 int retval = -1; /* default: fail */
5249#ifdef FEAT_MBYTE
5250 WCHAR *wn = NULL;
5251
5252 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005253 wn = enc_to_utf16(n, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005254#endif
5255
5256 if (mch_isdir(n))
5257 {
5258 char TempName[_MAX_PATH + 16] = "";
5259#ifdef FEAT_MBYTE
5260 WCHAR TempNameW[_MAX_PATH + 16] = L"";
5261#endif
5262
5263 if (p & R_OK)
5264 {
5265 /* Read check is performed by seeing if we can do a find file on
5266 * the directory for any file. */
5267#ifdef FEAT_MBYTE
5268 if (wn != NULL)
5269 {
5270 int i;
5271 WIN32_FIND_DATAW d;
5272
5273 for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i)
5274 TempNameW[i] = wn[i];
5275 if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/')
5276 TempNameW[i++] = '\\';
5277 TempNameW[i++] = '*';
5278 TempNameW[i++] = 0;
5279
5280 hFile = FindFirstFileW(TempNameW, &d);
5281 if (hFile == INVALID_HANDLE_VALUE)
5282 {
5283 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
5284 goto getout;
5285
5286 /* Retry with non-wide function (for Windows 98). */
5287 vim_free(wn);
5288 wn = NULL;
5289 }
5290 else
5291 (void)FindClose(hFile);
5292 }
5293 if (wn == NULL)
5294#endif
5295 {
5296 char *pch;
5297 WIN32_FIND_DATA d;
5298
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +00005299 vim_strncpy(TempName, n, _MAX_PATH);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005300 pch = TempName + STRLEN(TempName) - 1;
5301 if (*pch != '\\' && *pch != '/')
5302 *++pch = '\\';
5303 *++pch = '*';
5304 *++pch = NUL;
5305
5306 hFile = FindFirstFile(TempName, &d);
5307 if (hFile == INVALID_HANDLE_VALUE)
5308 goto getout;
5309 (void)FindClose(hFile);
5310 }
5311 }
5312
5313 if (p & W_OK)
5314 {
5315 /* Trying to create a temporary file in the directory should catch
5316 * directories on read-only network shares. However, in
5317 * directories whose ACL allows writes but denies deletes will end
5318 * up keeping the temporary file :-(. */
5319#ifdef FEAT_MBYTE
5320 if (wn != NULL)
5321 {
5322 if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW))
5323 {
5324 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
5325 goto getout;
5326
5327 /* Retry with non-wide function (for Windows 98). */
5328 vim_free(wn);
5329 wn = NULL;
5330 }
5331 else
5332 DeleteFileW(TempNameW);
5333 }
5334 if (wn == NULL)
5335#endif
5336 {
5337 if (!GetTempFileName(n, "VIM", 0, TempName))
5338 goto getout;
5339 mch_remove((char_u *)TempName);
5340 }
5341 }
5342 }
5343 else
5344 {
5345 /* Trying to open the file for the required access does ACL, read-only
5346 * network share, and file attribute checks. */
5347 am = ((p & W_OK) ? GENERIC_WRITE : 0)
5348 | ((p & R_OK) ? GENERIC_READ : 0);
5349#ifdef FEAT_MBYTE
5350 if (wn != NULL)
5351 {
5352 hFile = CreateFileW(wn, am, 0, NULL, OPEN_EXISTING, 0, NULL);
5353 if (hFile == INVALID_HANDLE_VALUE
5354 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
5355 {
5356 /* Retry with non-wide function (for Windows 98). */
5357 vim_free(wn);
5358 wn = NULL;
5359 }
5360 }
5361 if (wn == NULL)
5362#endif
5363 hFile = CreateFile(n, am, 0, NULL, OPEN_EXISTING, 0, NULL);
5364 if (hFile == INVALID_HANDLE_VALUE)
5365 goto getout;
5366 CloseHandle(hFile);
5367 }
5368
5369 retval = 0; /* success */
5370getout:
5371#ifdef FEAT_MBYTE
5372 vim_free(wn);
5373#endif
5374 return retval;
5375}
5376
5377#if defined(FEAT_MBYTE) || defined(PROTO)
5378/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005379 * Version of open() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005380 */
5381 int
5382mch_open(char *name, int flags, int mode)
5383{
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005384 /* _wopen() does not work with Borland C 5.5: creates a read-only file. */
5385# ifndef __BORLANDC__
Bram Moolenaar071d4272004-06-13 20:20:40 +00005386 WCHAR *wn;
5387 int f;
5388
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005389 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005390 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005391 wn = enc_to_utf16(name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005392 if (wn != NULL)
5393 {
5394 f = _wopen(wn, flags, mode);
5395 vim_free(wn);
5396 if (f >= 0)
5397 return f;
5398 /* Retry with non-wide function (for Windows 98). Can't use
5399 * GetLastError() here and it's unclear what errno gets set to if
5400 * the _wopen() fails for missing wide functions. */
5401 }
5402 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005403# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005404
5405 return open(name, flags, mode);
5406}
5407
5408/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005409 * Version of fopen() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005410 */
5411 FILE *
5412mch_fopen(char *name, char *mode)
5413{
5414 WCHAR *wn, *wm;
5415 FILE *f = NULL;
5416
5417 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
5418# ifdef __BORLANDC__
5419 /* Wide functions of Borland C 5.5 do not work on Windows 98. */
5420 && g_PlatformId == VER_PLATFORM_WIN32_NT
5421# endif
5422 )
5423 {
Bram Moolenaare6a91fd2008-07-24 18:51:11 +00005424# if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0fde2902008-03-16 13:54:13 +00005425 /* Work around an annoying assertion in the Microsoft debug CRT
5426 * when mode's text/binary setting doesn't match _get_fmode(). */
5427 char newMode = mode[strlen(mode) - 1];
5428 int oldMode = 0;
5429
5430 _get_fmode(&oldMode);
5431 if (newMode == 't')
5432 _set_fmode(_O_TEXT);
5433 else if (newMode == 'b')
5434 _set_fmode(_O_BINARY);
5435# endif
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005436 wn = enc_to_utf16(name, NULL);
5437 wm = enc_to_utf16(mode, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005438 if (wn != NULL && wm != NULL)
5439 f = _wfopen(wn, wm);
5440 vim_free(wn);
5441 vim_free(wm);
Bram Moolenaar0fde2902008-03-16 13:54:13 +00005442
Bram Moolenaare6a91fd2008-07-24 18:51:11 +00005443# if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0fde2902008-03-16 13:54:13 +00005444 _set_fmode(oldMode);
5445# endif
5446
Bram Moolenaar071d4272004-06-13 20:20:40 +00005447 if (f != NULL)
5448 return f;
5449 /* Retry with non-wide function (for Windows 98). Can't use
5450 * GetLastError() here and it's unclear what errno gets set to if
5451 * the _wfopen() fails for missing wide functions. */
5452 }
5453
5454 return fopen(name, mode);
5455}
5456#endif
5457
5458#ifdef FEAT_MBYTE
5459/*
5460 * SUB STREAM (aka info stream) handling:
5461 *
5462 * NTFS can have sub streams for each file. Normal contents of file is
5463 * stored in the main stream, and extra contents (author information and
5464 * title and so on) can be stored in sub stream. After Windows 2000, user
5465 * can access and store those informations in sub streams via explorer's
5466 * property menuitem in right click menu. Those informations in sub streams
5467 * were lost when copying only the main stream. So we have to copy sub
5468 * streams.
5469 *
5470 * Incomplete explanation:
5471 * http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp
5472 * More useful info and an example:
5473 * http://www.sysinternals.com/ntw2k/source/misc.shtml#streams
5474 */
5475
5476/*
5477 * Copy info stream data "substream". Read from the file with BackupRead(sh)
5478 * and write to stream "substream" of file "to".
5479 * Errors are ignored.
5480 */
5481 static void
5482copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len)
5483{
5484 HANDLE hTo;
5485 WCHAR *to_name;
5486
5487 to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR));
5488 wcscpy(to_name, to);
5489 wcscat(to_name, substream);
5490
5491 hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
5492 FILE_ATTRIBUTE_NORMAL, NULL);
5493 if (hTo != INVALID_HANDLE_VALUE)
5494 {
5495 long done;
5496 DWORD todo;
5497 DWORD readcnt, written;
5498 char buf[4096];
5499
5500 /* Copy block of bytes at a time. Abort when something goes wrong. */
5501 for (done = 0; done < len; done += written)
5502 {
5503 /* (size_t) cast for Borland C 5.5 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00005504 todo = (DWORD)((size_t)(len - done) > sizeof(buf) ? sizeof(buf)
5505 : (size_t)(len - done));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005506 if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt,
5507 FALSE, FALSE, context)
5508 || readcnt != todo
5509 || !WriteFile(hTo, buf, todo, &written, NULL)
5510 || written != todo)
5511 break;
5512 }
5513 CloseHandle(hTo);
5514 }
5515
5516 free(to_name);
5517}
5518
5519/*
5520 * Copy info streams from file "from" to file "to".
5521 */
5522 static void
5523copy_infostreams(char_u *from, char_u *to)
5524{
5525 WCHAR *fromw;
5526 WCHAR *tow;
5527 HANDLE sh;
5528 WIN32_STREAM_ID sid;
5529 int headersize;
5530 WCHAR streamname[_MAX_PATH];
5531 DWORD readcount;
5532 void *context = NULL;
5533 DWORD lo, hi;
5534 int len;
5535
5536 /* Convert the file names to wide characters. */
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005537 fromw = enc_to_utf16(from, NULL);
5538 tow = enc_to_utf16(to, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539 if (fromw != NULL && tow != NULL)
5540 {
5541 /* Open the file for reading. */
5542 sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL,
5543 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
5544 if (sh != INVALID_HANDLE_VALUE)
5545 {
5546 /* Use BackupRead() to find the info streams. Repeat until we
5547 * have done them all.*/
5548 for (;;)
5549 {
5550 /* Get the header to find the length of the stream name. If
5551 * the "readcount" is zero we have done all info streams. */
5552 ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00005553 headersize = (int)((char *)&sid.cStreamName - (char *)&sid.dwStreamId);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005554 if (!BackupRead(sh, (LPBYTE)&sid, headersize,
5555 &readcount, FALSE, FALSE, &context)
5556 || readcount == 0)
5557 break;
5558
5559 /* We only deal with streams that have a name. The normal
5560 * file data appears to be without a name, even though docs
5561 * suggest it is called "::$DATA". */
5562 if (sid.dwStreamNameSize > 0)
5563 {
5564 /* Read the stream name. */
5565 if (!BackupRead(sh, (LPBYTE)streamname,
5566 sid.dwStreamNameSize,
5567 &readcount, FALSE, FALSE, &context))
5568 break;
5569
5570 /* Copy an info stream with a name ":anything:$DATA".
5571 * Skip "::$DATA", it has no stream name (examples suggest
5572 * it might be used for the normal file contents).
5573 * Note that BackupRead() counts bytes, but the name is in
5574 * wide characters. */
5575 len = readcount / sizeof(WCHAR);
5576 streamname[len] = 0;
5577 if (len > 7 && wcsicmp(streamname + len - 6,
5578 L":$DATA") == 0)
5579 {
5580 streamname[len - 6] = 0;
5581 copy_substream(sh, &context, tow, streamname,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00005582 (long)sid.Size.u.LowPart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005583 }
5584 }
5585
5586 /* Advance to the next stream. We might try seeking too far,
5587 * but BackupSeek() doesn't skip over stream borders, thus
5588 * that's OK. */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00005589 (void)BackupSeek(sh, sid.Size.u.LowPart, sid.Size.u.HighPart,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005590 &lo, &hi, &context);
5591 }
5592
5593 /* Clear the context. */
5594 (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context);
5595
5596 CloseHandle(sh);
5597 }
5598 }
5599 vim_free(fromw);
5600 vim_free(tow);
5601}
5602#endif
5603
5604/*
5605 * Copy file attributes from file "from" to file "to".
5606 * For Windows NT and later we copy info streams.
5607 * Always returns zero, errors are ignored.
5608 */
5609 int
5610mch_copy_file_attribute(char_u *from, char_u *to)
5611{
5612#ifdef FEAT_MBYTE
5613 /* File streams only work on Windows NT and later. */
5614 PlatformId();
5615 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
5616 copy_infostreams(from, to);
5617#endif
5618 return 0;
5619}
5620
5621#if defined(MYRESETSTKOFLW) || defined(PROTO)
5622/*
5623 * Recreate a destroyed stack guard page in win32.
5624 * Written by Benjamin Peterson.
5625 */
5626
5627/* These magic numbers are from the MS header files */
5628#define MIN_STACK_WIN9X 17
5629#define MIN_STACK_WINNT 2
5630
5631/*
5632 * This function does the same thing as _resetstkoflw(), which is only
5633 * available in DevStudio .net and later.
5634 * Returns 0 for failure, 1 for success.
5635 */
5636 int
5637myresetstkoflw(void)
5638{
5639 BYTE *pStackPtr;
5640 BYTE *pGuardPage;
5641 BYTE *pStackBase;
5642 BYTE *pLowestPossiblePage;
5643 MEMORY_BASIC_INFORMATION mbi;
5644 SYSTEM_INFO si;
5645 DWORD nPageSize;
5646 DWORD dummy;
5647
5648 /* This code will not work on win32s. */
5649 PlatformId();
5650 if (g_PlatformId == VER_PLATFORM_WIN32s)
5651 return 0;
5652
5653 /* We need to know the system page size. */
5654 GetSystemInfo(&si);
5655 nPageSize = si.dwPageSize;
5656
5657 /* ...and the current stack pointer */
5658 pStackPtr = (BYTE*)_alloca(1);
5659
5660 /* ...and the base of the stack. */
5661 if (VirtualQuery(pStackPtr, &mbi, sizeof mbi) == 0)
5662 return 0;
5663 pStackBase = (BYTE*)mbi.AllocationBase;
5664
5665 /* ...and the page thats min_stack_req pages away from stack base; this is
5666 * the lowest page we could use. */
5667 pLowestPossiblePage = pStackBase + ((g_PlatformId == VER_PLATFORM_WIN32_NT)
5668 ? MIN_STACK_WINNT : MIN_STACK_WIN9X) * nPageSize;
5669
5670 /* On Win95, we want the next page down from the end of the stack. */
5671 if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS)
5672 {
5673 /* Find the page that's only 1 page down from the page that the stack
5674 * ptr is in. */
5675 pGuardPage = (BYTE*)((DWORD)nPageSize * (((DWORD)pStackPtr
5676 / (DWORD)nPageSize) - 1));
5677 if (pGuardPage < pLowestPossiblePage)
5678 return 0;
5679
5680 /* Apply the noaccess attribute to the page -- there's no guard
5681 * attribute in win95-type OSes. */
5682 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_NOACCESS, &dummy))
5683 return 0;
5684 }
5685 else
5686 {
5687 /* On NT, however, we want the first committed page in the stack Start
5688 * at the stack base and move forward through memory until we find a
5689 * committed block. */
5690 BYTE *pBlock = pStackBase;
5691
Bram Moolenaara466c992005-07-09 21:03:22 +00005692 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693 {
5694 if (VirtualQuery(pBlock, &mbi, sizeof mbi) == 0)
5695 return 0;
5696
5697 pBlock += mbi.RegionSize;
5698
5699 if (mbi.State & MEM_COMMIT)
5700 break;
5701 }
5702
5703 /* mbi now describes the first committed block in the stack. */
5704 if (mbi.Protect & PAGE_GUARD)
5705 return 1;
5706
5707 /* decide where the guard page should start */
5708 if ((long_u)(mbi.BaseAddress) < (long_u)pLowestPossiblePage)
5709 pGuardPage = pLowestPossiblePage;
5710 else
5711 pGuardPage = (BYTE*)mbi.BaseAddress;
5712
5713 /* allocate the guard page */
5714 if (!VirtualAlloc(pGuardPage, nPageSize, MEM_COMMIT, PAGE_READWRITE))
5715 return 0;
5716
5717 /* apply the guard attribute to the page */
5718 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_READWRITE | PAGE_GUARD,
5719 &dummy))
5720 return 0;
5721 }
5722
5723 return 1;
5724}
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005725#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005726
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005727
5728#if defined(FEAT_MBYTE) || defined(PROTO)
5729/*
5730 * The command line arguments in UCS2
5731 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00005732static int nArgsW = 0;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005733static LPWSTR *ArglistW = NULL;
5734static int global_argc = 0;
5735static char **global_argv;
5736
5737static int used_file_argc = 0; /* last argument in global_argv[] used
5738 for the argument list. */
5739static int *used_file_indexes = NULL; /* indexes in global_argv[] for
5740 command line arguments added to
5741 the argument list */
5742static int used_file_count = 0; /* nr of entries in used_file_indexes */
5743static int used_file_literal = FALSE; /* take file names literally */
5744static int used_file_full_path = FALSE; /* file name was full path */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005745static int used_file_diff_mode = FALSE; /* file name was with diff mode */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005746static int used_alist_count = 0;
5747
5748
5749/*
5750 * Get the command line arguments. Unicode version.
5751 * Returns argc. Zero when something fails.
5752 */
5753 int
5754get_cmd_argsW(char ***argvp)
5755{
5756 char **argv = NULL;
5757 int argc = 0;
5758 int i;
5759
5760 ArglistW = CommandLineToArgvW(GetCommandLineW(), &nArgsW);
5761 if (ArglistW != NULL)
5762 {
5763 argv = malloc((nArgsW + 1) * sizeof(char *));
5764 if (argv != NULL)
5765 {
5766 argc = nArgsW;
5767 argv[argc] = NULL;
5768 for (i = 0; i < argc; ++i)
5769 {
5770 int len;
5771
5772 /* Convert each Unicode argument to the current codepage. */
5773 WideCharToMultiByte_alloc(GetACP(), 0,
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00005774 ArglistW[i], (int)wcslen(ArglistW[i]) + 1,
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005775 (LPSTR *)&argv[i], &len, 0, 0);
5776 if (argv[i] == NULL)
5777 {
5778 /* Out of memory, clear everything. */
5779 while (i > 0)
5780 free(argv[--i]);
5781 free(argv);
5782 argc = 0;
5783 }
5784 }
5785 }
5786 }
5787
5788 global_argc = argc;
5789 global_argv = argv;
5790 if (argc > 0)
5791 used_file_indexes = malloc(argc * sizeof(int));
5792
5793 if (argvp != NULL)
5794 *argvp = argv;
5795 return argc;
5796}
5797
5798 void
5799free_cmd_argsW(void)
5800{
5801 if (ArglistW != NULL)
5802 {
5803 GlobalFree(ArglistW);
5804 ArglistW = NULL;
5805 }
5806}
5807
5808/*
5809 * Remember "name" is an argument that was added to the argument list.
5810 * This avoids that we have to re-parse the argument list when fix_arg_enc()
5811 * is called.
5812 */
5813 void
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005814used_file_arg(char *name, int literal, int full_path, int diff_mode)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005815{
5816 int i;
5817
5818 if (used_file_indexes == NULL)
5819 return;
5820 for (i = used_file_argc + 1; i < global_argc; ++i)
5821 if (STRCMP(global_argv[i], name) == 0)
5822 {
5823 used_file_argc = i;
5824 used_file_indexes[used_file_count++] = i;
5825 break;
5826 }
5827 used_file_literal = literal;
5828 used_file_full_path = full_path;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005829 used_file_diff_mode = diff_mode;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005830}
5831
5832/*
5833 * Remember the length of the argument list as it was. If it changes then we
5834 * leave it alone when 'encoding' is set.
5835 */
5836 void
5837set_alist_count(void)
5838{
5839 used_alist_count = GARGCOUNT;
5840}
5841
5842/*
5843 * Fix the encoding of the command line arguments. Invoked when 'encoding'
5844 * has been changed while starting up. Use the UCS-2 command line arguments
5845 * and convert them to 'encoding'.
5846 */
5847 void
5848fix_arg_enc(void)
5849{
5850 int i;
5851 int idx;
5852 char_u *str;
Bram Moolenaar86b68352004-12-27 21:59:20 +00005853 int *fnum_list;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005854
5855 /* Safety checks:
5856 * - if argument count differs between the wide and non-wide argument
5857 * list, something must be wrong.
5858 * - the file name arguments must have been located.
5859 * - the length of the argument list wasn't changed by the user.
5860 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00005861 if (global_argc != nArgsW
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005862 || ArglistW == NULL
5863 || used_file_indexes == NULL
5864 || used_file_count == 0
5865 || used_alist_count != GARGCOUNT)
5866 return;
5867
Bram Moolenaar86b68352004-12-27 21:59:20 +00005868 /* Remember the buffer numbers for the arguments. */
5869 fnum_list = (int *)alloc((int)sizeof(int) * GARGCOUNT);
5870 if (fnum_list == NULL)
5871 return; /* out of memory */
5872 for (i = 0; i < GARGCOUNT; ++i)
5873 fnum_list[i] = GARGLIST[i].ae_fnum;
5874
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005875 /* Clear the argument list. Make room for the new arguments. */
5876 alist_clear(&global_alist);
5877 if (ga_grow(&global_alist.al_ga, used_file_count) == FAIL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00005878 return; /* out of memory */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005879
5880 for (i = 0; i < used_file_count; ++i)
5881 {
5882 idx = used_file_indexes[i];
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005883 str = utf16_to_enc(ArglistW[idx], NULL);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005884 if (str != NULL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00005885 {
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005886#ifdef FEAT_DIFF
5887 /* When using diff mode may need to concatenate file name to
5888 * directory name. Just like it's done in main(). */
5889 if (used_file_diff_mode && mch_isdir(str) && GARGCOUNT > 0
5890 && !mch_isdir(alist_name(&GARGLIST[0])))
5891 {
5892 char_u *r;
5893
5894 r = concat_fnames(str, gettail(alist_name(&GARGLIST[0])), TRUE);
5895 if (r != NULL)
5896 {
5897 vim_free(str);
5898 str = r;
5899 }
5900 }
5901#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00005902 /* Re-use the old buffer by renaming it. When not using literal
5903 * names it's done by alist_expand() below. */
5904 if (used_file_literal)
5905 buf_set_name(fnum_list[i], str);
5906
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005907 alist_add(&global_alist, str, used_file_literal ? 2 : 0);
Bram Moolenaar86b68352004-12-27 21:59:20 +00005908 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005909 }
5910
5911 if (!used_file_literal)
5912 {
5913 /* Now expand wildcards in the arguments. */
5914 /* Temporarily add '(' and ')' to 'isfname'. These are valid
5915 * filename characters but are excluded from 'isfname' to make
5916 * "gf" work on a file name in parenthesis (e.g.: see vim.h). */
5917 do_cmdline_cmd((char_u *)":let SaVe_ISF = &isf|set isf+=(,)");
Bram Moolenaar86b68352004-12-27 21:59:20 +00005918 alist_expand(fnum_list, used_alist_count);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005919 do_cmdline_cmd((char_u *)":let &isf = SaVe_ISF|unlet SaVe_ISF");
5920 }
5921
5922 /* If wildcard expansion failed, we are editing the first file of the
5923 * arglist and there is no file name: Edit the first argument now. */
5924 if (curwin->w_arg_idx == 0 && curbuf->b_fname == NULL)
5925 {
5926 do_cmdline_cmd((char_u *)":rewind");
5927 if (GARGCOUNT == 1 && used_file_full_path)
5928 (void)vim_chdirfile(alist_name(&GARGLIST[0]));
5929 }
Bram Moolenaar86b68352004-12-27 21:59:20 +00005930
5931 set_alist_count();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005932}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005933#endif