blob: 74b8960325e416644479ac483ac8a2cc84af2159 [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
23#include <io.h>
24#include "vim.h"
25
26#ifdef HAVE_FCNTL_H
27# include <fcntl.h>
28#endif
29#include <sys/types.h>
30#include <errno.h>
31#include <signal.h>
32#include <limits.h>
33#include <process.h>
34
35#undef chdir
36#ifdef __GNUC__
37# ifndef __MINGW32__
38# include <dirent.h>
39# endif
40#else
41# include <direct.h>
42#endif
43
44#if defined(FEAT_TITLE) && !defined(FEAT_GUI_W32)
45# include <shellapi.h>
46#endif
47
48#ifdef __MINGW32__
49# ifndef FROM_LEFT_1ST_BUTTON_PRESSED
50# define FROM_LEFT_1ST_BUTTON_PRESSED 0x0001
51# endif
52# ifndef RIGHTMOST_BUTTON_PRESSED
53# define RIGHTMOST_BUTTON_PRESSED 0x0002
54# endif
55# ifndef FROM_LEFT_2ND_BUTTON_PRESSED
56# define FROM_LEFT_2ND_BUTTON_PRESSED 0x0004
57# endif
58# ifndef FROM_LEFT_3RD_BUTTON_PRESSED
59# define FROM_LEFT_3RD_BUTTON_PRESSED 0x0008
60# endif
61# ifndef FROM_LEFT_4TH_BUTTON_PRESSED
62# define FROM_LEFT_4TH_BUTTON_PRESSED 0x0010
63# endif
64
65/*
66 * EventFlags
67 */
68# ifndef MOUSE_MOVED
69# define MOUSE_MOVED 0x0001
70# endif
71# ifndef DOUBLE_CLICK
72# define DOUBLE_CLICK 0x0002
73# endif
74#endif
75
76/* Record all output and all keyboard & mouse input */
77/* #define MCH_WRITE_DUMP */
78
79#ifdef MCH_WRITE_DUMP
80FILE* fdDump = NULL;
81#endif
82
83/*
84 * When generating prototypes for Win32 on Unix, these lines make the syntax
85 * errors disappear. They do not need to be correct.
86 */
87#ifdef PROTO
88#define WINAPI
89#define WINBASEAPI
90typedef char * LPCSTR;
91typedef 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
147PFNGCKLN s_pfnGetConsoleKeyboardLayoutName = NULL;
148#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
155#ifndef FEAT_GUI_W32
156/* Win32 Console handles for input and output */
157static HANDLE g_hConIn = INVALID_HANDLE_VALUE;
158static HANDLE g_hConOut = INVALID_HANDLE_VALUE;
159
160/* Win32 Screen buffer,coordinate,console I/O information */
161static SMALL_RECT g_srScrollRegion;
162static COORD g_coord; /* 0-based, but external coords are 1-based */
163
164/* The attribute of the screen when the editor was started */
165static WORD g_attrDefault = 7; /* lightgray text on black background */
166static WORD g_attrCurrent;
167
168static int g_fCBrkPressed = FALSE; /* set by ctrl-break interrupt */
169static int g_fCtrlCPressed = FALSE; /* set when ctrl-C or ctrl-break detected */
170static int g_fForceExit = FALSE; /* set when forcefully exiting */
171
172static void termcap_mode_start(void);
173static void termcap_mode_end(void);
174static void clear_chars(COORD coord, DWORD n);
175static void clear_screen(void);
176static void clear_to_end_of_display(void);
177static void clear_to_end_of_line(void);
178static void scroll(unsigned cLines);
179static void set_scroll_region(unsigned left, unsigned top,
180 unsigned right, unsigned bottom);
181static void insert_lines(unsigned cLines);
182static void delete_lines(unsigned cLines);
183static void gotoxy(unsigned x, unsigned y);
184static void normvideo(void);
185static void textattr(WORD wAttr);
186static void textcolor(WORD wAttr);
187static void textbackground(WORD wAttr);
188static void standout(void);
189static void standend(void);
190static void visual_bell(void);
191static void cursor_visible(BOOL fVisible);
192static BOOL write_chars(LPCSTR pchBuf, DWORD cchToWrite);
193static char_u tgetch(int *pmodifiers, char_u *pch2);
194static void create_conin(void);
195static int s_cursor_visible = TRUE;
196static int did_create_conin = FALSE;
197#else
198static int s_dont_use_vimrun = TRUE;
199static int need_vimrun_warning = FALSE;
200static char *vimrun_path = "vimrun ";
201#endif
202
203#ifndef FEAT_GUI_W32
204static int suppress_winsize = 1; /* don't fiddle with console */
205#endif
206
207 static void
208get_exe_name(void)
209{
210 char temp[256];
211
212 if (exe_name == NULL)
213 {
214 /* store the name of the executable, may be used for $VIM */
215 GetModuleFileName(NULL, temp, 255);
216 if (*temp != NUL)
217 exe_name = FullName_save((char_u *)temp, FALSE);
218 }
219}
220
221#if defined(DYNAMIC_GETTEXT) || defined(PROTO)
222# ifndef GETTEXT_DLL
223# define GETTEXT_DLL "libintl.dll"
224# endif
225/* Dummy funcitons */
226static char* null_libintl_gettext(const char *);
227static char* null_libintl_textdomain(const char *);
228static char* null_libintl_bindtextdomain(const char *, const char *);
229
230static HINSTANCE hLibintlDLL = 0;
231char* (*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
232char* (*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
233char* (*dyn_libintl_bindtextdomain)(const char *, const char *)
234 = null_libintl_bindtextdomain;
235
236 int
237dyn_libintl_init(char *libname)
238{
239 int i;
240 static struct
241 {
242 char *name;
243 FARPROC *ptr;
244 } libintl_entry[] =
245 {
246 {"gettext", (FARPROC*)&dyn_libintl_gettext},
247 {"textdomain", (FARPROC*)&dyn_libintl_textdomain},
248 {"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
249 {NULL, NULL}
250 };
251
252 /* No need to initialize twice. */
253 if (hLibintlDLL)
254 return 1;
255 /* Load gettext library (libintl.dll) */
256 hLibintlDLL = LoadLibrary(libname != NULL ? libname : GETTEXT_DLL);
257 if (!hLibintlDLL)
258 {
259 char_u dirname[_MAX_PATH];
260
261 /* Try using the path from gvim.exe to find the .dll there. */
262 get_exe_name();
263 STRCPY(dirname, exe_name);
264 STRCPY(gettail(dirname), GETTEXT_DLL);
265 hLibintlDLL = LoadLibrary((char *)dirname);
266 if (!hLibintlDLL)
267 {
268 if (p_verbose > 0)
269 EMSG2(_(e_loadlib), GETTEXT_DLL);
270 return 0;
271 }
272 }
273 for (i = 0; libintl_entry[i].name != NULL
274 && libintl_entry[i].ptr != NULL; ++i)
275 {
276 if ((*libintl_entry[i].ptr = (FARPROC)GetProcAddress(hLibintlDLL,
277 libintl_entry[i].name)) == NULL)
278 {
279 dyn_libintl_end();
280 if (p_verbose > 0)
281 EMSG2(_(e_loadfunc), libintl_entry[i].name);
282 return 0;
283 }
284 }
285 return 1;
286}
287
288 void
289dyn_libintl_end()
290{
291 if (hLibintlDLL)
292 FreeLibrary(hLibintlDLL);
293 hLibintlDLL = NULL;
294 dyn_libintl_gettext = null_libintl_gettext;
295 dyn_libintl_textdomain = null_libintl_textdomain;
296 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain;
297}
298
299 static char *
300null_libintl_gettext(const char* msgid)
301{
302 return (char*)msgid;
303}
304
305 static char *
306null_libintl_bindtextdomain(const char* domainname, const char* dirname)
307{
308 return NULL;
309}
310
311 static char *
312null_libintl_textdomain(const char* domainname)
313{
314 return NULL;
315}
316
317#endif /* DYNAMIC_GETTEXT */
318
319/* This symbol is not defined in older versions of the SDK or Visual C++ */
320
321#ifndef VER_PLATFORM_WIN32_WINDOWS
322# define VER_PLATFORM_WIN32_WINDOWS 1
323#endif
324
325DWORD g_PlatformId;
326
327#ifdef HAVE_ACL
328# include <aclapi.h>
329/*
330 * These are needed to dynamically load the ADVAPI DLL, which is not
331 * implemented under Windows 95 (and causes VIM to crash)
332 */
333typedef DWORD (WINAPI *PSNSECINFO) (LPTSTR, enum SE_OBJECT_TYPE,
334 SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
335typedef DWORD (WINAPI *PGNSECINFO) (LPSTR, enum SE_OBJECT_TYPE,
336 SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *,
337 PSECURITY_DESCRIPTOR *);
338
339static HANDLE advapi_lib = NULL; /* Handle for ADVAPI library */
340static PSNSECINFO pSetNamedSecurityInfo;
341static PGNSECINFO pGetNamedSecurityInfo;
342#endif
343
344/*
345 * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
346 * VER_PLATFORM_WIN32_WINDOWS (Win95).
347 */
348 void
349PlatformId(void)
350{
351 static int done = FALSE;
352
353 if (!done)
354 {
355 OSVERSIONINFO ovi;
356
357 ovi.dwOSVersionInfoSize = sizeof(ovi);
358 GetVersionEx(&ovi);
359
360 g_PlatformId = ovi.dwPlatformId;
361
362#ifdef HAVE_ACL
363 /*
364 * Load the ADVAPI runtime if we are on anything
365 * other than Windows 95
366 */
367 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
368 {
369 /*
370 * do this load. Problems: Doesn't unload at end of run (this is
371 * theoretically okay, since Windows should unload it when VIM
372 * terminates). Should we be using the 'mch_libcall' routines?
373 * Seems like a lot of overhead to load/unload ADVAPI32.DLL each
374 * time we verify security...
375 */
376 advapi_lib = LoadLibrary("ADVAPI32.DLL");
377 if (advapi_lib != NULL)
378 {
379 pSetNamedSecurityInfo = (PSNSECINFO)GetProcAddress(advapi_lib,
380 "SetNamedSecurityInfoA");
381 pGetNamedSecurityInfo = (PGNSECINFO)GetProcAddress(advapi_lib,
382 "GetNamedSecurityInfoA");
383 if (pSetNamedSecurityInfo == NULL
384 || pGetNamedSecurityInfo == NULL)
385 {
386 /* If we can't get the function addresses, set advapi_lib
387 * to NULL so that we don't use them. */
388 FreeLibrary(advapi_lib);
389 advapi_lib = NULL;
390 }
391 }
392 }
393#endif
394 done = TRUE;
395 }
396}
397
398/*
399 * Return TRUE when running on Windows 95 (or 98 or ME).
400 * Only to be used after mch_init().
401 */
402 int
403mch_windows95(void)
404{
405 return g_PlatformId == VER_PLATFORM_WIN32_WINDOWS;
406}
407
408#ifdef FEAT_GUI_W32
409/*
410 * Used to work around the "can't do synchronous spawn"
411 * problem on Win32s, without resorting to Universal Thunk.
412 */
413static int old_num_windows;
414static int num_windows;
415
416 static BOOL CALLBACK
417win32ssynch_cb(HWND hwnd, LPARAM lparam)
418{
419 num_windows++;
420 return TRUE;
421}
422#endif
423
424#ifndef FEAT_GUI_W32
425
426#define SHIFT (SHIFT_PRESSED)
427#define CTRL (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
428#define ALT (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
429#define ALT_GR (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)
430
431
432/* When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
433 * We map function keys to their ANSI terminal equivalents, as produced
434 * by ANSI.SYS, for compatibility with the MS-DOS version of Vim. Any
435 * ANSI key with a value >= '\300' is nonstandard, but provided anyway
436 * so that the user can have access to all SHIFT-, CTRL-, and ALT-
437 * combinations of function/arrow/etc keys.
438 */
439
440const static struct
441{
442 WORD wVirtKey;
443 BOOL fAnsiKey;
444 int chAlone;
445 int chShift;
446 int chCtrl;
447 int chAlt;
448} VirtKeyMap[] =
449{
450
451/* Key ANSI alone shift ctrl alt */
452 { VK_ESCAPE,FALSE, ESC, ESC, ESC, ESC, },
453
454 { VK_F1, TRUE, ';', 'T', '^', 'h', },
455 { VK_F2, TRUE, '<', 'U', '_', 'i', },
456 { VK_F3, TRUE, '=', 'V', '`', 'j', },
457 { VK_F4, TRUE, '>', 'W', 'a', 'k', },
458 { VK_F5, TRUE, '?', 'X', 'b', 'l', },
459 { VK_F6, TRUE, '@', 'Y', 'c', 'm', },
460 { VK_F7, TRUE, 'A', 'Z', 'd', 'n', },
461 { VK_F8, TRUE, 'B', '[', 'e', 'o', },
462 { VK_F9, TRUE, 'C', '\\', 'f', 'p', },
463 { VK_F10, TRUE, 'D', ']', 'g', 'q', },
464 { VK_F11, TRUE, '\205', '\207', '\211', '\213', },
465 { VK_F12, TRUE, '\206', '\210', '\212', '\214', },
466
467 { VK_HOME, TRUE, 'G', '\302', 'w', '\303', },
468 { VK_UP, TRUE, 'H', '\304', '\305', '\306', },
469 { VK_PRIOR, TRUE, 'I', '\307', '\204', '\310', }, /*PgUp*/
470 { VK_LEFT, TRUE, 'K', '\311', 's', '\312', },
471 { VK_RIGHT, TRUE, 'M', '\313', 't', '\314', },
472 { VK_END, TRUE, 'O', '\315', 'u', '\316', },
473 { VK_DOWN, TRUE, 'P', '\317', '\320', '\321', },
474 { VK_NEXT, TRUE, 'Q', '\322', 'v', '\323', }, /*PgDn*/
475 { VK_INSERT,TRUE, 'R', '\324', '\325', '\326', },
476 { VK_DELETE,TRUE, 'S', '\327', '\330', '\331', },
477
478 { VK_SNAPSHOT,TRUE, 0, 0, 0, 'r', }, /*PrtScrn*/
479
480#if 0
481 /* Most people don't have F13-F20, but what the hell... */
482 { VK_F13, TRUE, '\332', '\333', '\334', '\335', },
483 { VK_F14, TRUE, '\336', '\337', '\340', '\341', },
484 { VK_F15, TRUE, '\342', '\343', '\344', '\345', },
485 { VK_F16, TRUE, '\346', '\347', '\350', '\351', },
486 { VK_F17, TRUE, '\352', '\353', '\354', '\355', },
487 { VK_F18, TRUE, '\356', '\357', '\360', '\361', },
488 { VK_F19, TRUE, '\362', '\363', '\364', '\365', },
489 { VK_F20, TRUE, '\366', '\367', '\370', '\371', },
490#endif
491 { VK_ADD, TRUE, 'N', 'N', 'N', 'N', }, /* keyp '+' */
492 { VK_SUBTRACT, TRUE,'J', 'J', 'J', 'J', }, /* keyp '-' */
493 /* { VK_DIVIDE, TRUE,'N', 'N', 'N', 'N', }, keyp '/' */
494 { VK_MULTIPLY, TRUE,'7', '7', '7', '7', }, /* keyp '*' */
495
496 { VK_NUMPAD0,TRUE, '\332', '\333', '\334', '\335', },
497 { VK_NUMPAD1,TRUE, '\336', '\337', '\340', '\341', },
498 { VK_NUMPAD2,TRUE, '\342', '\343', '\344', '\345', },
499 { VK_NUMPAD3,TRUE, '\346', '\347', '\350', '\351', },
500 { VK_NUMPAD4,TRUE, '\352', '\353', '\354', '\355', },
501 { VK_NUMPAD5,TRUE, '\356', '\357', '\360', '\361', },
502 { VK_NUMPAD6,TRUE, '\362', '\363', '\364', '\365', },
503 { VK_NUMPAD7,TRUE, '\366', '\367', '\370', '\371', },
504 { VK_NUMPAD8,TRUE, '\372', '\373', '\374', '\375', },
505 /* Sorry, out of number space! <negri>*/
506 { VK_NUMPAD9,TRUE, '\376', '\377', '\377', '\367', },
507
508};
509
510
511#ifdef _MSC_VER
512// The ToAscii bug destroys several registers. Need to turn off optimization
513// or the GetConsoleKeyboardLayoutName hack will fail in non-debug versions
514# pragma optimize("", off)
515#endif
516
517#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__)
518# define AChar AsciiChar
519#else
520# define AChar uChar.AsciiChar
521#endif
522
523/* The return code indicates key code size. */
524 static int
525#ifdef __BORLANDC__
526 __stdcall
527#endif
528win32_kbd_patch_key(
529 KEY_EVENT_RECORD* pker)
530{
531 UINT uMods = pker->dwControlKeyState;
532 static int s_iIsDead = 0;
533 static WORD awAnsiCode[2];
534 static BYTE abKeystate[256];
535
536
537 if (s_iIsDead == 2)
538 {
539 pker->AChar = (CHAR) awAnsiCode[1];
540 s_iIsDead = 0;
541 return 1;
542 }
543
544 if (pker->AChar != 0)
545 return 1;
546
547 memset(abKeystate, 0, sizeof (abKeystate));
548
549 // Should only be non-NULL on NT 4.0
550 if (s_pfnGetConsoleKeyboardLayoutName != NULL)
551 {
552 CHAR szKLID[KL_NAMELENGTH];
553
554 if ((*s_pfnGetConsoleKeyboardLayoutName)(szKLID))
555 (void)LoadKeyboardLayout(szKLID, KLF_ACTIVATE);
556 }
557
558 /* Clear any pending dead keys */
559 ToAscii(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 0);
560
561 if (uMods & SHIFT_PRESSED)
562 abKeystate[VK_SHIFT] = 0x80;
563 if (uMods & CAPSLOCK_ON)
564 abKeystate[VK_CAPITAL] = 1;
565
566 if ((uMods & ALT_GR) == ALT_GR)
567 {
568 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
569 abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
570 }
571
572 s_iIsDead = ToAscii(pker->wVirtualKeyCode, pker->wVirtualScanCode,
573 abKeystate, awAnsiCode, 0);
574
575 if (s_iIsDead > 0)
576 pker->AChar = (CHAR) awAnsiCode[0];
577
578 return s_iIsDead;
579}
580
581#ifdef _MSC_VER
582/* MUST switch optimization on again here, otherwise a call to
583 * decode_key_event() may crash (e.g. when hitting caps-lock) */
584# pragma optimize("", on)
585
586# if (_MSC_VER < 1100)
587/* MUST turn off global optimisation for this next function, or
588 * pressing ctrl-minus in insert mode crashes Vim when built with
589 * VC4.1. -- negri. */
590# pragma optimize("g", off)
591# endif
592#endif
593
594static BOOL g_fJustGotFocus = FALSE;
595
596/*
597 * Decode a KEY_EVENT into one or two keystrokes
598 */
599 static BOOL
600decode_key_event(
601 KEY_EVENT_RECORD *pker,
602 char_u *pch,
603 char_u *pch2,
604 int *pmodifiers,
605 BOOL fDoPost)
606{
607 int i;
608 const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
609
610 *pch = *pch2 = NUL;
611 g_fJustGotFocus = FALSE;
612
613 /* ignore key up events */
614 if (!pker->bKeyDown)
615 return FALSE;
616
617 /* ignore some keystrokes */
618 switch (pker->wVirtualKeyCode)
619 {
620 /* modifiers */
621 case VK_SHIFT:
622 case VK_CONTROL:
623 case VK_MENU: /* Alt key */
624 return FALSE;
625
626 default:
627 break;
628 }
629
630 /* special cases */
631 if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->AChar == NUL)
632 {
633 /* Ctrl-6 is Ctrl-^ */
634 if (pker->wVirtualKeyCode == '6')
635 {
636 *pch = Ctrl_HAT;
637 return TRUE;
638 }
639 /* Ctrl-2 is Ctrl-@ */
640 else if (pker->wVirtualKeyCode == '2')
641 {
642 *pch = NUL;
643 return TRUE;
644 }
645 /* Ctrl-- is Ctrl-_ */
646 else if (pker->wVirtualKeyCode == 0xBD)
647 {
648 *pch = Ctrl__;
649 return TRUE;
650 }
651 }
652
653 /* Shift-TAB */
654 if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED))
655 {
656 *pch = K_NUL;
657 *pch2 = '\017';
658 return TRUE;
659 }
660
661 for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]); --i >= 0; )
662 {
663 if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
664 {
665 if (nModifs == 0)
666 *pch = VirtKeyMap[i].chAlone;
667 else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
668 *pch = VirtKeyMap[i].chShift;
669 else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
670 *pch = VirtKeyMap[i].chCtrl;
671 else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
672 *pch = VirtKeyMap[i].chAlt;
673
674 if (*pch != 0)
675 {
676 if (VirtKeyMap[i].fAnsiKey)
677 {
678 *pch2 = *pch;
679 *pch = K_NUL;
680 }
681
682 return TRUE;
683 }
684 }
685 }
686
687 i = win32_kbd_patch_key(pker);
688
689 if (i < 0)
690 *pch = NUL;
691 else
692 {
693 *pch = (i > 0) ? pker->AChar : NUL;
694
695 if (pmodifiers != NULL)
696 {
697 /* Pass on the ALT key as a modifier, but only when not combined
698 * with CTRL (which is ALTGR). */
699 if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0)
700 *pmodifiers |= MOD_MASK_ALT;
701
702 /* Pass on SHIFT only for special keys, because we don't know when
703 * it's already included with the character. */
704 if ((nModifs & SHIFT) != 0 && *pch <= 0x20)
705 *pmodifiers |= MOD_MASK_SHIFT;
706
707 /* Pass on CTRL only for non-special keys, because we don't know
708 * when it's already included with the character. And not when
709 * combined with ALT (which is ALTGR). */
710 if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0
711 && *pch >= 0x20 && *pch < 0x80)
712 *pmodifiers |= MOD_MASK_CTRL;
713 }
714 }
715
716 return (*pch != NUL);
717}
718
719#ifdef _MSC_VER
720# pragma optimize("", on)
721#endif
722
723#endif /* FEAT_GUI_W32 */
724
725
726#ifdef FEAT_MOUSE
727
728/*
729 * For the GUI the mouse handling is in gui_w32.c.
730 */
731# ifdef FEAT_GUI_W32
732 void
733mch_setmouse(
734 int on)
735{
736}
737# else
738static int g_fMouseAvail = FALSE; /* mouse present */
739static int g_fMouseActive = FALSE; /* mouse enabled */
740static int g_nMouseClick = -1; /* mouse status */
741static int g_xMouse; /* mouse x coordinate */
742static int g_yMouse; /* mouse y coordinate */
743
744/*
745 * Enable or disable mouse input
746 */
747 void
748mch_setmouse(
749 int on)
750{
751 DWORD cmodein;
752
753 if (!g_fMouseAvail)
754 return;
755
756 g_fMouseActive = on;
757 GetConsoleMode(g_hConIn, &cmodein);
758
759 if (g_fMouseActive)
760 cmodein |= ENABLE_MOUSE_INPUT;
761 else
762 cmodein &= ~ENABLE_MOUSE_INPUT;
763
764 SetConsoleMode(g_hConIn, cmodein);
765}
766
767
768/*
769 * Decode a MOUSE_EVENT. If it's a valid event, return MOUSE_LEFT,
770 * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
771 * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
772 * or a MOUSE_LEFT, _MIDDLE, or _RIGHT. We encode the button type,
773 * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
774 * and we return the mouse position in g_xMouse and g_yMouse.
775 *
776 * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
777 * MOUSE_DRAGs and one MOUSE_RELEASE. MOUSE_RELEASE will be followed only
778 * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
779 *
780 * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
781 * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
782 *
783 * Windows will send us MOUSE_MOVED notifications whenever the mouse
784 * moves, even if it stays within the same character cell. We ignore
785 * all MOUSE_MOVED messages if the position hasn't really changed, and
786 * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
787 * we're only interested in MOUSE_DRAG).
788 *
789 * All of this is complicated by the code that fakes MOUSE_MIDDLE on
790 * 2-button mouses by pressing the left & right buttons simultaneously.
791 * In practice, it's almost impossible to click both at the same time,
792 * so we need to delay a little. Also, we tend not to get MOUSE_RELEASE
793 * in such cases, if the user is clicking quickly.
794 */
795 static BOOL
796decode_mouse_event(
797 MOUSE_EVENT_RECORD* pmer)
798{
799 static int s_nOldButton = -1;
800 static int s_nOldMouseClick = -1;
801 static int s_xOldMouse = -1;
802 static int s_yOldMouse = -1;
803 static linenr_T s_old_topline = 0;
804#ifdef FEAT_DIFF
805 static int s_old_topfill = 0;
806#endif
807 static int s_cClicks = 1;
808 static BOOL s_fReleased = TRUE;
809 static DWORD s_dwLastClickTime = 0;
810 static BOOL s_fNextIsMiddle = FALSE;
811
812 static DWORD cButtons = 0; /* number of buttons supported */
813
814 const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
815 const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
816 const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
817 const DWORD LEFT_RIGHT = LEFT | RIGHT;
818
819 int nButton;
820
821 if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons))
822 cButtons = 2;
823
824 if (!g_fMouseAvail || !g_fMouseActive)
825 {
826 g_nMouseClick = -1;
827 return FALSE;
828 }
829
830 /* get a spurious MOUSE_EVENT immediately after receiving focus; ignore */
831 if (g_fJustGotFocus)
832 {
833 g_fJustGotFocus = FALSE;
834 return FALSE;
835 }
836
837 /* unprocessed mouse click? */
838 if (g_nMouseClick != -1)
839 return TRUE;
840
841 nButton = -1;
842 g_xMouse = pmer->dwMousePosition.X;
843 g_yMouse = pmer->dwMousePosition.Y;
844
845 if (pmer->dwEventFlags == MOUSE_MOVED)
846 {
847 /* ignore MOUSE_MOVED events if (x, y) hasn't changed. (We get these
848 * events even when the mouse moves only within a char cell.) */
849 if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse)
850 return FALSE;
851 }
852
853 /* If no buttons are pressed... */
854 if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0)
855 {
856 /* If the last thing returned was MOUSE_RELEASE, ignore this */
857 if (s_fReleased)
858 return FALSE;
859
860 nButton = MOUSE_RELEASE;
861 s_fReleased = TRUE;
862 }
863 else /* one or more buttons pressed */
864 {
865 /* on a 2-button mouse, hold down left and right buttons
866 * simultaneously to get MIDDLE. */
867
868 if (cButtons == 2 && s_nOldButton != MOUSE_DRAG)
869 {
870 DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
871
872 /* if either left or right button only is pressed, see if the
873 * the next mouse event has both of them pressed */
874 if (dwLR == LEFT || dwLR == RIGHT)
875 {
876 for (;;)
877 {
878 /* wait a short time for next input event */
879 if (WaitForSingleObject(g_hConIn, p_mouset / 3)
880 != WAIT_OBJECT_0)
881 break;
882 else
883 {
884 DWORD cRecords = 0;
885 INPUT_RECORD ir;
886 MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
887
888 PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
889
890 if (cRecords == 0 || ir.EventType != MOUSE_EVENT
891 || !(pmer2->dwButtonState & LEFT_RIGHT))
892 break;
893 else
894 {
895 if (pmer2->dwEventFlags != MOUSE_MOVED)
896 {
897 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
898
899 return decode_mouse_event(pmer2);
900 }
901 else if (s_xOldMouse == pmer2->dwMousePosition.X &&
902 s_yOldMouse == pmer2->dwMousePosition.Y)
903 {
904 /* throw away spurious mouse move */
905 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
906
907 /* are there any more mouse events in queue? */
908 PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
909
910 if (cRecords==0 || ir.EventType != MOUSE_EVENT)
911 break;
912 }
913 else
914 break;
915 }
916 }
917 }
918 }
919 }
920
921 if (s_fNextIsMiddle)
922 {
923 nButton = (pmer->dwEventFlags == MOUSE_MOVED)
924 ? MOUSE_DRAG : MOUSE_MIDDLE;
925 s_fNextIsMiddle = FALSE;
926 }
927 else if (cButtons == 2 &&
928 ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
929 {
930 nButton = MOUSE_MIDDLE;
931
932 if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED)
933 {
934 s_fNextIsMiddle = TRUE;
935 nButton = MOUSE_RELEASE;
936 }
937 }
938 else if ((pmer->dwButtonState & LEFT) == LEFT)
939 nButton = MOUSE_LEFT;
940 else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
941 nButton = MOUSE_MIDDLE;
942 else if ((pmer->dwButtonState & RIGHT) == RIGHT)
943 nButton = MOUSE_RIGHT;
944
945 if (! s_fReleased && ! s_fNextIsMiddle
946 && nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG)
947 return FALSE;
948
949 s_fReleased = s_fNextIsMiddle;
950 }
951
952 if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK)
953 {
954 /* button pressed or released, without mouse moving */
955 if (nButton != -1 && nButton != MOUSE_RELEASE)
956 {
957 DWORD dwCurrentTime = GetTickCount();
958
959 if (s_xOldMouse != g_xMouse
960 || s_yOldMouse != g_yMouse
961 || s_nOldButton != nButton
962 || s_old_topline != curwin->w_topline
963#ifdef FEAT_DIFF
964 || s_old_topfill != curwin->w_topfill
965#endif
966 || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset)
967 {
968 s_cClicks = 1;
969 }
970 else if (++s_cClicks > 4)
971 {
972 s_cClicks = 1;
973 }
974
975 s_dwLastClickTime = dwCurrentTime;
976 }
977 }
978 else if (pmer->dwEventFlags == MOUSE_MOVED)
979 {
980 if (nButton != -1 && nButton != MOUSE_RELEASE)
981 nButton = MOUSE_DRAG;
982
983 s_cClicks = 1;
984 }
985
986 if (nButton == -1)
987 return FALSE;
988
989 if (nButton != MOUSE_RELEASE)
990 s_nOldButton = nButton;
991
992 g_nMouseClick = nButton;
993
994 if (pmer->dwControlKeyState & SHIFT_PRESSED)
995 g_nMouseClick |= MOUSE_SHIFT;
996 if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
997 g_nMouseClick |= MOUSE_CTRL;
998 if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
999 g_nMouseClick |= MOUSE_ALT;
1000
1001 if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE)
1002 SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
1003
1004 /* only pass on interesting (i.e., different) mouse events */
1005 if (s_xOldMouse == g_xMouse
1006 && s_yOldMouse == g_yMouse
1007 && s_nOldMouseClick == g_nMouseClick)
1008 {
1009 g_nMouseClick = -1;
1010 return FALSE;
1011 }
1012
1013 s_xOldMouse = g_xMouse;
1014 s_yOldMouse = g_yMouse;
1015 s_old_topline = curwin->w_topline;
1016#ifdef FEAT_DIFF
1017 s_old_topfill = curwin->w_topfill;
1018#endif
1019 s_nOldMouseClick = g_nMouseClick;
1020
1021 return TRUE;
1022}
1023
1024# endif /* FEAT_GUI_W32 */
1025#endif /* FEAT_MOUSE */
1026
1027
1028#ifdef MCH_CURSOR_SHAPE
1029/*
1030 * Set the shape of the cursor.
1031 * 'thickness' can be from 1 (thin) to 99 (block)
1032 */
1033 static void
1034mch_set_cursor_shape(int thickness)
1035{
1036 CONSOLE_CURSOR_INFO ConsoleCursorInfo;
1037 ConsoleCursorInfo.dwSize = thickness;
1038 ConsoleCursorInfo.bVisible = s_cursor_visible;
1039
1040 SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo);
1041 if (s_cursor_visible)
1042 SetConsoleCursorPosition(g_hConOut, g_coord);
1043}
1044
1045 void
1046mch_update_cursor(void)
1047{
1048 int idx;
1049 int thickness;
1050
1051 /*
1052 * How the cursor is drawn depends on the current mode.
1053 */
1054 idx = get_shape_idx(FALSE);
1055
1056 if (shape_table[idx].shape == SHAPE_BLOCK)
1057 thickness = 99; /* 100 doesn't work on W95 */
1058 else
1059 thickness = shape_table[idx].percentage;
1060 mch_set_cursor_shape(thickness);
1061}
1062#endif
1063
1064#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1065/*
1066 * Handle FOCUS_EVENT.
1067 */
1068 static void
1069handle_focus_event(INPUT_RECORD ir)
1070{
1071 g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
1072 ui_focus_change((int)g_fJustGotFocus);
1073}
1074
1075/*
1076 * Wait until console input from keyboard or mouse is available,
1077 * or the time is up.
1078 * Return TRUE if something is available FALSE if not.
1079 */
1080 static int
1081WaitForChar(long msec)
1082{
1083 DWORD dwNow = 0, dwEndTime = 0;
1084 INPUT_RECORD ir;
1085 DWORD cRecords;
1086 char_u ch, ch2;
1087
1088 if (msec > 0)
1089 /* Wait until the specified time has elapsed. */
1090 dwEndTime = GetTickCount() + msec;
1091 else if (msec < 0)
1092 /* Wait forever. */
1093 dwEndTime = INFINITE;
1094
1095 /* We need to loop until the end of the time period, because
1096 * we might get multiple unusable mouse events in that time.
1097 */
1098 for (;;)
1099 {
1100#ifdef FEAT_CLIENTSERVER
1101 serverProcessPendingMessages();
1102#endif
1103 if (0
1104#ifdef FEAT_MOUSE
1105 || g_nMouseClick != -1
1106#endif
1107#ifdef FEAT_CLIENTSERVER
1108 || input_available()
1109#endif
1110 )
1111 return TRUE;
1112
1113 if (msec > 0)
1114 {
1115 /* If the specified wait time has passed, return. */
1116 dwNow = GetTickCount();
1117 if (dwNow >= dwEndTime)
1118 break;
1119 }
1120 if (msec != 0)
1121 {
1122#ifdef FEAT_CLIENTSERVER
1123 /* Wait for either an event on the console input or a message in
1124 * the client-server window. */
1125 if (MsgWaitForMultipleObjects(1, &g_hConIn, FALSE,
1126 dwEndTime - dwNow, QS_SENDMESSAGE) != WAIT_OBJECT_0)
1127#else
1128 if (WaitForSingleObject(g_hConIn, dwEndTime - dwNow)
1129 != WAIT_OBJECT_0)
1130#endif
1131 continue;
1132 }
1133
1134 cRecords = 0;
1135 PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
1136
1137#ifdef FEAT_MBYTE_IME
1138 if (State & CMDLINE && msg_row == Rows - 1)
1139 {
1140 CONSOLE_SCREEN_BUFFER_INFO csbi;
1141
1142 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1143 {
1144 if (csbi.dwCursorPosition.Y != msg_row)
1145 {
1146 /* The screen is now messed up, must redraw the
1147 * command line and later all the windows. */
1148 redraw_all_later(CLEAR);
1149 cmdline_row -= (msg_row - csbi.dwCursorPosition.Y);
1150 redrawcmd();
1151 }
1152 }
1153 }
1154#endif
1155
1156 if (cRecords > 0)
1157 {
1158 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
1159 {
1160#ifdef FEAT_MBYTE_IME
1161 /* Windows IME sends two '\n's with only one 'ENTER'. First:
1162 * wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */
1163 if (ir.Event.KeyEvent.uChar.UnicodeChar == 0
1164 && ir.Event.KeyEvent.wVirtualKeyCode == 13)
1165 {
1166 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
1167 continue;
1168 }
1169#endif
1170 if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
1171 NULL, FALSE))
1172 return TRUE;
1173 }
1174
1175 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
1176
1177 if (ir.EventType == FOCUS_EVENT)
1178 handle_focus_event(ir);
1179 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1180 shell_resized();
1181#ifdef FEAT_MOUSE
1182 else if (ir.EventType == MOUSE_EVENT
1183 && decode_mouse_event(&ir.Event.MouseEvent))
1184 return TRUE;
1185#endif
1186 }
1187 else if (msec == 0)
1188 break;
1189 }
1190
1191#ifdef FEAT_CLIENTSERVER
1192 /* Something might have been received while we were waiting. */
1193 if (input_available())
1194 return TRUE;
1195#endif
1196 return FALSE;
1197}
1198
1199#ifndef FEAT_GUI_MSWIN
1200/*
1201 * return non-zero if a character is available
1202 */
1203 int
1204mch_char_avail()
1205{
1206 return WaitForChar(0L);
1207}
1208#endif
1209
1210/*
1211 * Create the console input. Used when reading stdin doesn't work.
1212 */
1213 static void
1214create_conin(void)
1215{
1216 g_hConIn = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
1217 FILE_SHARE_READ|FILE_SHARE_WRITE,
1218 (LPSECURITY_ATTRIBUTES) NULL,
1219 OPEN_EXISTING, (DWORD)NULL, (HANDLE)NULL);
1220 did_create_conin = TRUE;
1221}
1222
1223/*
1224 * Get a keystroke or a mouse event
1225 */
1226 static char_u
1227tgetch(int *pmodifiers, char_u *pch2)
1228{
1229 char_u ch;
1230
1231 for (;;)
1232 {
1233 INPUT_RECORD ir;
1234 DWORD cRecords = 0;
1235
1236#ifdef FEAT_CLIENTSERVER
1237 (void)WaitForChar(-1L);
1238 if (input_available())
1239 return 0;
1240# ifdef FEAT_MOUSE
1241 if (g_nMouseClick != -1)
1242 return 0;
1243# endif
1244#endif
1245 if (ReadConsoleInput(g_hConIn, &ir, 1, &cRecords) == 0)
1246 {
1247 if (did_create_conin)
1248 read_error_exit();
1249 create_conin();
1250 continue;
1251 }
1252
1253 if (ir.EventType == KEY_EVENT)
1254 {
1255 if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2,
1256 pmodifiers, TRUE))
1257 return ch;
1258 }
1259 else if (ir.EventType == FOCUS_EVENT)
1260 handle_focus_event(ir);
1261 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
1262 shell_resized();
1263#ifdef FEAT_MOUSE
1264 else if (ir.EventType == MOUSE_EVENT)
1265 {
1266 if (decode_mouse_event(&ir.Event.MouseEvent))
1267 return 0;
1268 }
1269#endif
1270 }
1271}
1272#endif /* !FEAT_GUI_W32 */
1273
1274
1275/*
1276 * mch_inchar(): low-level input funcion.
1277 * Get one or more characters from the keyboard or the mouse.
1278 * If time == 0, do not wait for characters.
1279 * If time == n, wait a short time for characters.
1280 * If time == -1, wait forever for characters.
1281 * Returns the number of characters read into buf.
1282 */
1283 int
1284mch_inchar(
1285 char_u *buf,
1286 int maxlen,
1287 long time,
1288 int tb_change_cnt)
1289{
1290#ifndef FEAT_GUI_W32 /* this isn't used for the GUI */
1291
1292 int len;
1293 int c;
1294#ifdef FEAT_AUTOCMD
1295 static int once_already = 0;
1296#endif
1297#define TYPEAHEADLEN 20
1298 static char_u typeahead[TYPEAHEADLEN]; /* previously typed bytes. */
1299 static int typeaheadlen = 0;
1300
1301 /* First use any typeahead that was kept because "buf" was too small. */
1302 if (typeaheadlen > 0)
1303 goto theend;
1304
1305#ifdef FEAT_SNIFF
1306 if (want_sniff_request)
1307 {
1308 if (sniff_request_waiting)
1309 {
1310 /* return K_SNIFF */
1311 typeahead[typeaheadlen++] = CSI;
1312 typeahead[typeaheadlen++] = (char_u)KS_EXTRA;
1313 typeahead[typeaheadlen++] = (char_u)KE_SNIFF;
1314 sniff_request_waiting = 0;
1315 want_sniff_request = 0;
1316 goto theend;
1317 }
1318 else if (time < 0 || time > 250)
1319 {
1320 /* don't wait too long, a request might be pending */
1321 time = 250;
1322 }
1323 }
1324#endif
1325
1326 if (time >= 0)
1327 {
1328 if (!WaitForChar(time)) /* no character available */
1329 {
1330#ifdef FEAT_AUTOCMD
1331 once_already = 0;
1332#endif
1333 return 0;
1334 }
1335 }
1336 else /* time == -1, wait forever */
1337 {
1338 mch_set_winsize_now(); /* Allow winsize changes from now on */
1339
1340#ifdef FEAT_AUTOCMD
1341 /* If there is no character available within 2 seconds (default),
1342 * write the autoscript file to disk */
1343 if (once_already == 2)
1344 updatescript(0);
1345 else if (once_already == 1)
1346 {
1347 setcursor();
1348 once_already = 2;
1349 return 0;
1350 }
1351 else
1352#endif
1353 if (!WaitForChar(p_ut))
1354 {
1355#ifdef FEAT_AUTOCMD
1356 if (has_cursorhold() && get_real_state() == NORMAL_BUSY)
1357 {
1358 apply_autocmds(EVENT_CURSORHOLD, NULL, NULL, FALSE, curbuf);
1359 update_screen(VALID);
1360 once_already = 1;
1361 return 0;
1362 }
1363#endif
1364 updatescript(0);
1365 }
1366 }
1367
1368 /*
1369 * Try to read as many characters as there are, until the buffer is full.
1370 */
1371
1372 /* we will get at least one key. Get more if they are available. */
1373 g_fCBrkPressed = FALSE;
1374
1375#ifdef MCH_WRITE_DUMP
1376 if (fdDump)
1377 fputc('[', fdDump);
1378#endif
1379
1380 /* Keep looping until there is something in the typeahead buffer and more
1381 * to get and still room in the buffer (up to two bytes for a char and
1382 * three bytes for a modifier). */
1383 while ((typeaheadlen == 0 || WaitForChar(0L))
1384 && typeaheadlen + 5 <= TYPEAHEADLEN)
1385 {
1386 if (typebuf_changed(tb_change_cnt))
1387 {
1388 /* "buf" may be invalid now if a client put something in the
1389 * typeahead buffer and "buf" is in the typeahead buffer. */
1390 typeaheadlen = 0;
1391 break;
1392 }
1393#ifdef FEAT_MOUSE
1394 if (g_nMouseClick != -1)
1395 {
1396# ifdef MCH_WRITE_DUMP
1397 if (fdDump)
1398 fprintf(fdDump, "{%02x @ %d, %d}",
1399 g_nMouseClick, g_xMouse, g_yMouse);
1400# endif
1401 typeahead[typeaheadlen++] = ESC + 128;
1402 typeahead[typeaheadlen++] = 'M';
1403 typeahead[typeaheadlen++] = g_nMouseClick;
1404 typeahead[typeaheadlen++] = g_xMouse + '!';
1405 typeahead[typeaheadlen++] = g_yMouse + '!';
1406 g_nMouseClick = -1;
1407 }
1408 else
1409#endif
1410 {
1411 char_u ch2 = NUL;
1412 int modifiers = 0;
1413
1414 c = tgetch(&modifiers, &ch2);
1415
1416 if (typebuf_changed(tb_change_cnt))
1417 {
1418 /* "buf" may be invalid now if a client put something in the
1419 * typeahead buffer and "buf" is in the typeahead buffer. */
1420 typeaheadlen = 0;
1421 break;
1422 }
1423
1424 if (c == Ctrl_C && ctrl_c_interrupts)
1425 {
1426#if defined(FEAT_CLIENTSERVER)
1427 trash_input_buf();
1428#endif
1429 got_int = TRUE;
1430 }
1431
1432#ifdef FEAT_MOUSE
1433 if (g_nMouseClick == -1)
1434#endif
1435 {
1436 int n = 1;
1437
1438 /* A key may have one or two bytes. */
1439 typeahead[typeaheadlen] = c;
1440 if (ch2 != NUL)
1441 {
1442 typeahead[typeaheadlen + 1] = ch2;
1443 ++n;
1444 }
1445#ifdef FEAT_MBYTE
1446 /* Only convert normal characters, not special keys. Need to
1447 * convert before applying ALT, otherwise mapping <M-x> breaks
1448 * when 'tenc' is set. */
1449 if (input_conv.vc_type != CONV_NONE
1450 && (ch2 == NUL || c != K_NUL))
1451 n = convert_input(typeahead + typeaheadlen, n,
1452 TYPEAHEADLEN - typeaheadlen);
1453#endif
1454
1455 /* Use the ALT key to set the 8th bit of the character
1456 * when it's one byte, the 8th bit isn't set yet and not
1457 * using a double-byte encoding (would become a lead
1458 * byte). */
1459 if ((modifiers & MOD_MASK_ALT)
1460 && n == 1
1461 && (typeahead[typeaheadlen] & 0x80) == 0
1462#ifdef FEAT_MBYTE
1463 && !enc_dbcs
1464#endif
1465 )
1466 {
1467 typeahead[typeaheadlen] |= 0x80;
1468 modifiers &= ~MOD_MASK_ALT;
1469 }
1470
1471 if (modifiers != 0)
1472 {
1473 /* Prepend modifiers to the character. */
1474 mch_memmove(typeahead + typeaheadlen + 3,
1475 typeahead + typeaheadlen, n);
1476 typeahead[typeaheadlen++] = K_SPECIAL;
1477 typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
1478 typeahead[typeaheadlen++] = modifiers;
1479 }
1480
1481 typeaheadlen += n;
1482
1483#ifdef MCH_WRITE_DUMP
1484 if (fdDump)
1485 fputc(c, fdDump);
1486#endif
1487 }
1488 }
1489 }
1490
1491#ifdef MCH_WRITE_DUMP
1492 if (fdDump)
1493 {
1494 fputs("]\n", fdDump);
1495 fflush(fdDump);
1496 }
1497#endif
1498
1499#ifdef FEAT_AUTOCMD
1500 once_already = 0;
1501#endif
1502
1503theend:
1504 /* Move typeahead to "buf", as much as fits. */
1505 len = 0;
1506 while (len < maxlen && typeaheadlen > 0)
1507 {
1508 buf[len++] = typeahead[0];
1509 mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
1510 }
1511 return len;
1512
1513#else /* FEAT_GUI_W32 */
1514 return 0;
1515#endif /* FEAT_GUI_W32 */
1516}
1517
1518#ifndef __MINGW32__
1519# include <shellapi.h> /* required for FindExecutable() */
1520#endif
1521
1522 static int
1523executable_exists(char *name)
1524{
1525 char location[2 * _MAX_PATH + 2];
1526 char widename[2 * _MAX_PATH];
1527
1528 /* There appears to be a bug in FindExecutableA() on Windows NT.
1529 * Use FindExecutableW() instead... */
1530 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
1531 {
1532 MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1,
1533 (LPWSTR)widename, _MAX_PATH);
1534 if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"",
1535 (LPWSTR)location) > (HINSTANCE)32)
1536 return TRUE;
1537 }
1538 else
1539 {
1540 if (FindExecutableA((LPCTSTR)name, (LPCTSTR)"",
1541 (LPTSTR)location) > (HINSTANCE)32)
1542 return TRUE;
1543 }
1544 return FALSE;
1545}
1546
1547#ifdef FEAT_GUI_W32
1548
1549/*
1550 * GUI version of mch_init().
1551 */
1552 void
1553mch_init()
1554{
1555#ifndef __MINGW32__
1556 extern int _fmode;
1557#endif
1558
1559 /* Let critical errors result in a failure, not in a dialog box. Required
1560 * for the timestamp test to work on removed floppies. */
1561 SetErrorMode(SEM_FAILCRITICALERRORS);
1562
1563 _fmode = O_BINARY; /* we do our own CR-LF translation */
1564
1565 /* Specify window size. Is there a place to get the default from? */
1566 Rows = 25;
1567 Columns = 80;
1568
1569 /* Look for 'vimrun' */
1570 if (!gui_is_win32s())
1571 {
1572 char_u vimrun_location[_MAX_PATH + 4];
1573
1574 /* First try in same directory as gvim.exe */
1575 STRCPY(vimrun_location, exe_name);
1576 STRCPY(gettail(vimrun_location), "vimrun.exe");
1577 if (mch_getperm(vimrun_location) >= 0)
1578 {
1579 if (*skiptowhite(vimrun_location) != NUL)
1580 {
1581 /* Enclose path with white space in double quotes. */
1582 mch_memmove(vimrun_location + 1, vimrun_location,
1583 STRLEN(vimrun_location) + 1);
1584 *vimrun_location = '"';
1585 STRCPY(gettail(vimrun_location), "vimrun\" ");
1586 }
1587 else
1588 STRCPY(gettail(vimrun_location), "vimrun ");
1589
1590 vimrun_path = (char *)vim_strsave(vimrun_location);
1591 s_dont_use_vimrun = FALSE;
1592 }
1593 else if (executable_exists("vimrun.exe"))
1594 s_dont_use_vimrun = FALSE;
1595
1596 /* Don't give the warning for a missing vimrun.exe right now, but only
1597 * when vimrun was supposed to be used. Don't bother people that do
1598 * not need vimrun.exe. */
1599 if (s_dont_use_vimrun)
1600 need_vimrun_warning = TRUE;
1601 }
1602
1603 /*
1604 * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
1605 * Otherwise the default "findstr /n" is used.
1606 */
1607 if (!executable_exists("findstr.exe"))
1608 set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
1609
1610#ifdef FEAT_CLIPBOARD
1611 clip_init(TRUE);
1612
1613 /*
1614 * Vim's own clipboard format recognises whether the text is char, line, or
1615 * rectangular block. Only useful for copying between two Vims.
1616 * "VimClipboard" was used for previous versions, using the first
1617 * character to specify MCHAR, MLINE or MBLOCK.
1618 */
1619 clip_star.format = RegisterClipboardFormat("VimClipboard2");
1620 clip_star.format_raw = RegisterClipboardFormat("VimRawBytes");
1621#endif
1622}
1623
1624
1625#else /* FEAT_GUI_W32 */
1626
1627#define SRWIDTH(sr) ((sr).Right - (sr).Left + 1)
1628#define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1)
1629
1630/*
1631 * ClearConsoleBuffer()
1632 * Description:
1633 * Clears the entire contents of the console screen buffer, using the
1634 * specified attribute.
1635 * Returns:
1636 * TRUE on success
1637 */
1638 static BOOL
1639ClearConsoleBuffer(WORD wAttribute)
1640{
1641 CONSOLE_SCREEN_BUFFER_INFO csbi;
1642 COORD coord;
1643 DWORD NumCells, dummy;
1644
1645 if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1646 return FALSE;
1647
1648 NumCells = csbi.dwSize.X * csbi.dwSize.Y;
1649 coord.X = 0;
1650 coord.Y = 0;
1651 if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells,
1652 coord, &dummy))
1653 {
1654 return FALSE;
1655 }
1656 if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells,
1657 coord, &dummy))
1658 {
1659 return FALSE;
1660 }
1661
1662 return TRUE;
1663}
1664
1665/*
1666 * FitConsoleWindow()
1667 * Description:
1668 * Checks if the console window will fit within given buffer dimensions.
1669 * Also, if requested, will shrink the window to fit.
1670 * Returns:
1671 * TRUE on success
1672 */
1673 static BOOL
1674FitConsoleWindow(
1675 COORD dwBufferSize,
1676 BOOL WantAdjust)
1677{
1678 CONSOLE_SCREEN_BUFFER_INFO csbi;
1679 COORD dwWindowSize;
1680 BOOL NeedAdjust = FALSE;
1681
1682 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
1683 {
1684 /*
1685 * A buffer resize will fail if the current console window does
1686 * not lie completely within that buffer. To avoid this, we might
1687 * have to move and possibly shrink the window.
1688 */
1689 if (csbi.srWindow.Right >= dwBufferSize.X)
1690 {
1691 dwWindowSize.X = SRWIDTH(csbi.srWindow);
1692 if (dwWindowSize.X > dwBufferSize.X)
1693 dwWindowSize.X = dwBufferSize.X;
1694 csbi.srWindow.Right = dwBufferSize.X - 1;
1695 csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X;
1696 NeedAdjust = TRUE;
1697 }
1698 if (csbi.srWindow.Bottom >= dwBufferSize.Y)
1699 {
1700 dwWindowSize.Y = SRHEIGHT(csbi.srWindow);
1701 if (dwWindowSize.Y > dwBufferSize.Y)
1702 dwWindowSize.Y = dwBufferSize.Y;
1703 csbi.srWindow.Bottom = dwBufferSize.Y - 1;
1704 csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y;
1705 NeedAdjust = TRUE;
1706 }
1707 if (NeedAdjust && WantAdjust)
1708 {
1709 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow))
1710 return FALSE;
1711 }
1712 return TRUE;
1713 }
1714
1715 return FALSE;
1716}
1717
1718typedef struct ConsoleBufferStruct
1719{
1720 BOOL IsValid;
1721 CONSOLE_SCREEN_BUFFER_INFO Info;
1722 PCHAR_INFO Buffer;
1723 COORD BufferSize;
1724} ConsoleBuffer;
1725
1726/*
1727 * SaveConsoleBuffer()
1728 * Description:
1729 * Saves important information about the console buffer, including the
1730 * actual buffer contents. The saved information is suitable for later
1731 * restoration by RestoreConsoleBuffer().
1732 * Returns:
1733 * TRUE if all information was saved; FALSE otherwise
1734 * If FALSE, still sets cb->IsValid if buffer characteristics were saved.
1735 */
1736 static BOOL
1737SaveConsoleBuffer(
1738 ConsoleBuffer *cb)
1739{
1740 DWORD NumCells;
1741 COORD BufferCoord;
1742 SMALL_RECT ReadRegion;
1743 WORD Y, Y_incr;
1744
1745 if (cb == NULL)
1746 return FALSE;
1747
1748 if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
1749 {
1750 cb->IsValid = FALSE;
1751 return FALSE;
1752 }
1753 cb->IsValid = TRUE;
1754
1755 /*
1756 * Allocate a buffer large enough to hold the entire console screen
1757 * buffer. If this ConsoleBuffer structure has already been initialized
1758 * with a buffer of the correct size, then just use that one.
1759 */
1760 if (!cb->IsValid || cb->Buffer == NULL ||
1761 cb->BufferSize.X != cb->Info.dwSize.X ||
1762 cb->BufferSize.Y != cb->Info.dwSize.Y)
1763 {
1764 cb->BufferSize.X = cb->Info.dwSize.X;
1765 cb->BufferSize.Y = cb->Info.dwSize.Y;
1766 NumCells = cb->BufferSize.X * cb->BufferSize.Y;
1767 if (cb->Buffer != NULL)
1768 vim_free(cb->Buffer);
1769 cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
1770 if (cb->Buffer == NULL)
1771 return FALSE;
1772 }
1773
1774 /*
1775 * We will now copy the console screen buffer into our buffer.
1776 * ReadConsoleOutput() seems to be limited as far as how much you
1777 * can read at a time. Empirically, this number seems to be about
1778 * 12000 cells (rows * columns). Start at position (0, 0) and copy
1779 * in chunks until it is all copied. The chunks will all have the
1780 * same horizontal characteristics, so initialize them now. The
1781 * height of each chunk will be (12000 / width).
1782 */
1783 BufferCoord.X = 0;
1784 ReadRegion.Left = 0;
1785 ReadRegion.Right = cb->Info.dwSize.X - 1;
1786 Y_incr = 12000 / cb->Info.dwSize.X;
1787 for (Y = 0; Y < cb->BufferSize.Y; Y += Y_incr)
1788 {
1789 /*
1790 * Read into position (0, Y) in our buffer.
1791 */
1792 BufferCoord.Y = Y;
1793 /*
1794 * Read the region whose top left corner is (0, Y) and whose bottom
1795 * right corner is (width - 1, Y + Y_incr - 1). This should define
1796 * a region of size width by Y_incr. Don't worry if this region is
1797 * too large for the remaining buffer; it will be cropped.
1798 */
1799 ReadRegion.Top = Y;
1800 ReadRegion.Bottom = Y + Y_incr - 1;
1801 if (!ReadConsoleOutput(g_hConOut, /* output handle */
1802 cb->Buffer, /* our buffer */
1803 cb->BufferSize, /* dimensions of our buffer */
1804 BufferCoord, /* offset in our buffer */
1805 &ReadRegion)) /* region to save */
1806 {
1807 vim_free(cb->Buffer);
1808 cb->Buffer = NULL;
1809 return FALSE;
1810 }
1811 }
1812
1813 return TRUE;
1814}
1815
1816/*
1817 * RestoreConsoleBuffer()
1818 * Description:
1819 * Restores important information about the console buffer, including the
1820 * actual buffer contents, if desired. The information to restore is in
1821 * the same format used by SaveConsoleBuffer().
1822 * Returns:
1823 * TRUE on success
1824 */
1825 static BOOL
1826RestoreConsoleBuffer(
1827 ConsoleBuffer *cb,
1828 BOOL RestoreScreen)
1829{
1830 COORD BufferCoord;
1831 SMALL_RECT WriteRegion;
1832
1833 if (cb == NULL || !cb->IsValid)
1834 return FALSE;
1835
1836 /*
1837 * Before restoring the buffer contents, clear the current buffer, and
1838 * restore the cursor position and window information. Doing this now
1839 * prevents old buffer contents from "flashing" onto the screen.
1840 */
1841 if (RestoreScreen)
1842 ClearConsoleBuffer(cb->Info.wAttributes);
1843
1844 FitConsoleWindow(cb->Info.dwSize, TRUE);
1845 if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
1846 return FALSE;
1847 if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
1848 return FALSE;
1849
1850 if (!RestoreScreen)
1851 {
1852 /*
1853 * No need to restore the screen buffer contents, so we're done.
1854 */
1855 return TRUE;
1856 }
1857
1858 if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
1859 return FALSE;
1860 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
1861 return FALSE;
1862
1863 /*
1864 * Restore the screen buffer contents.
1865 */
1866 if (cb->Buffer != NULL)
1867 {
1868 BufferCoord.X = 0;
1869 BufferCoord.Y = 0;
1870 WriteRegion.Left = 0;
1871 WriteRegion.Top = 0;
1872 WriteRegion.Right = cb->Info.dwSize.X - 1;
1873 WriteRegion.Bottom = cb->Info.dwSize.Y - 1;
1874 if (!WriteConsoleOutput(g_hConOut, /* output handle */
1875 cb->Buffer, /* our buffer */
1876 cb->BufferSize, /* dimensions of our buffer */
1877 BufferCoord, /* offset in our buffer */
1878 &WriteRegion)) /* region to restore */
1879 {
1880 return FALSE;
1881 }
1882 }
1883
1884 return TRUE;
1885}
1886
1887#ifdef FEAT_RESTORE_ORIG_SCREEN
1888static ConsoleBuffer g_cbOrig = { 0 };
1889#endif
1890static ConsoleBuffer g_cbNonTermcap = { 0 };
1891static ConsoleBuffer g_cbTermcap = { 0 };
1892
1893#ifdef FEAT_TITLE
1894#ifdef __BORLANDC__
1895typedef HWND (__stdcall *GETCONSOLEWINDOWPROC)(VOID);
1896#else
1897typedef WINBASEAPI HWND (WINAPI *GETCONSOLEWINDOWPROC)(VOID);
1898#endif
1899char g_szOrigTitle[256] = { 0 };
1900HWND g_hWnd = NULL; /* also used in os_mswin.c */
1901static HICON g_hOrigIconSmall = NULL;
1902static HICON g_hOrigIcon = NULL;
1903static HICON g_hVimIcon = NULL;
1904static BOOL g_fCanChangeIcon = FALSE;
1905
1906/* ICON* are not defined in VC++ 4.0 */
1907#ifndef ICON_SMALL
1908#define ICON_SMALL 0
1909#endif
1910#ifndef ICON_BIG
1911#define ICON_BIG 1
1912#endif
1913/*
1914 * GetConsoleIcon()
1915 * Description:
1916 * Attempts to retrieve the small icon and/or the big icon currently in
1917 * use by a given window.
1918 * Returns:
1919 * TRUE on success
1920 */
1921 static BOOL
1922GetConsoleIcon(
1923 HWND hWnd,
1924 HICON *phIconSmall,
1925 HICON *phIcon)
1926{
1927 if (hWnd == NULL)
1928 return FALSE;
1929
1930 if (phIconSmall != NULL)
1931 {
1932 *phIconSmall = (HICON) SendMessage(hWnd, WM_GETICON,
1933 (WPARAM) ICON_SMALL, (LPARAM) 0);
1934 }
1935 if (phIcon != NULL)
1936 {
1937 *phIcon = (HICON) SendMessage(hWnd, WM_GETICON,
1938 (WPARAM) ICON_BIG, (LPARAM) 0);
1939 }
1940 return TRUE;
1941}
1942
1943/*
1944 * SetConsoleIcon()
1945 * Description:
1946 * Attempts to change the small icon and/or the big icon currently in
1947 * use by a given window.
1948 * Returns:
1949 * TRUE on success
1950 */
1951 static BOOL
1952SetConsoleIcon(
1953 HWND hWnd,
1954 HICON hIconSmall,
1955 HICON hIcon)
1956{
1957 HICON hPrevIconSmall;
1958 HICON hPrevIcon;
1959
1960 if (hWnd == NULL)
1961 return FALSE;
1962
1963 if (hIconSmall != NULL)
1964 {
1965 hPrevIconSmall = (HICON) SendMessage(hWnd, WM_SETICON,
1966 (WPARAM) ICON_SMALL, (LPARAM) hIconSmall);
1967 }
1968 if (hIcon != NULL)
1969 {
1970 hPrevIcon = (HICON) SendMessage(hWnd, WM_SETICON,
1971 (WPARAM) ICON_BIG, (LPARAM) hIcon);
1972 }
1973 return TRUE;
1974}
1975
1976/*
1977 * SaveConsoleTitleAndIcon()
1978 * Description:
1979 * Saves the current console window title in g_szOrigTitle, for later
1980 * restoration. Also, attempts to obtain a handle to the console window,
1981 * and use it to save the small and big icons currently in use by the
1982 * console window. This is not always possible on some versions of Windows;
1983 * nor is it possible when running Vim remotely using Telnet (since the
1984 * console window the user sees is owned by a remote process).
1985 */
1986 static void
1987SaveConsoleTitleAndIcon(void)
1988{
1989 GETCONSOLEWINDOWPROC GetConsoleWindowProc;
1990
1991 /* Save the original title. */
1992 if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle)))
1993 return;
1994
1995 /*
1996 * Obtain a handle to the console window using GetConsoleWindow() from
1997 * KERNEL32.DLL; we need to handle in order to change the window icon.
1998 * This function only exists on NT-based Windows, starting with Windows
1999 * 2000. On older operating systems, we can't change the window icon
2000 * anyway.
2001 */
2002 if ((GetConsoleWindowProc = (GETCONSOLEWINDOWPROC)
2003 GetProcAddress(GetModuleHandle("KERNEL32.DLL"),
2004 "GetConsoleWindow")) != NULL)
2005 {
2006 g_hWnd = (*GetConsoleWindowProc)();
2007 }
2008 if (g_hWnd == NULL)
2009 return;
2010
2011 /* Save the original console window icon. */
2012 GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon);
2013 if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL)
2014 return;
2015
2016 /* Extract the first icon contained in the Vim executable. */
2017 g_hVimIcon = ExtractIcon(NULL, exe_name, 0);
2018 if (g_hVimIcon != NULL)
2019 g_fCanChangeIcon = TRUE;
2020}
2021#endif
2022
2023static int g_fWindInitCalled = FALSE;
2024static int g_fTermcapMode = FALSE;
2025static CONSOLE_CURSOR_INFO g_cci;
2026static DWORD g_cmodein = 0;
2027static DWORD g_cmodeout = 0;
2028
2029/*
2030 * non-GUI version of mch_init().
2031 */
2032 void
2033mch_init()
2034{
2035#ifndef FEAT_RESTORE_ORIG_SCREEN
2036 CONSOLE_SCREEN_BUFFER_INFO csbi;
2037#endif
2038#ifndef __MINGW32__
2039 extern int _fmode;
2040#endif
2041
2042 /* Let critical errors result in a failure, not in a dialog box. Required
2043 * for the timestamp test to work on removed floppies. */
2044 SetErrorMode(SEM_FAILCRITICALERRORS);
2045
2046 _fmode = O_BINARY; /* we do our own CR-LF translation */
2047 out_flush();
2048
2049 /* Obtain handles for the standard Console I/O devices */
2050 if (read_cmd_fd == 0)
2051 g_hConIn = GetStdHandle(STD_INPUT_HANDLE);
2052 else
2053 create_conin();
2054 g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
2055
2056#ifdef FEAT_RESTORE_ORIG_SCREEN
2057 /* Save the initial console buffer for later restoration */
2058 SaveConsoleBuffer(&g_cbOrig);
2059 g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
2060#else
2061 /* Get current text attributes */
2062 GetConsoleScreenBufferInfo(g_hConOut, &csbi);
2063 g_attrCurrent = g_attrDefault = csbi.wAttributes;
2064#endif
2065 if (cterm_normal_fg_color == 0)
2066 cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
2067 if (cterm_normal_bg_color == 0)
2068 cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
2069
2070 /* set termcap codes to current text attributes */
2071 update_tcap(g_attrCurrent);
2072
2073 GetConsoleCursorInfo(g_hConOut, &g_cci);
2074 GetConsoleMode(g_hConIn, &g_cmodein);
2075 GetConsoleMode(g_hConOut, &g_cmodeout);
2076
2077#ifdef FEAT_TITLE
2078 SaveConsoleTitleAndIcon();
2079 /*
2080 * Set both the small and big icons of the console window to Vim's icon.
2081 * Note that Vim presently only has one size of icon (32x32), but it
2082 * automatically gets scaled down to 16x16 when setting the small icon.
2083 */
2084 if (g_fCanChangeIcon)
2085 SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon);
2086#endif
2087
2088 ui_get_shellsize();
2089
2090#ifdef MCH_WRITE_DUMP
2091 fdDump = fopen("dump", "wt");
2092
2093 if (fdDump)
2094 {
2095 time_t t;
2096
2097 time(&t);
2098 fputs(ctime(&t), fdDump);
2099 fflush(fdDump);
2100 }
2101#endif
2102
2103 g_fWindInitCalled = TRUE;
2104
2105#ifdef FEAT_MOUSE
2106 g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
2107#endif
2108
2109#ifdef FEAT_CLIPBOARD
2110 clip_init(TRUE);
2111
2112 /*
2113 * Vim's own clipboard format recognises whether the text is char, line, or
2114 * rectangular block. Only useful for copying between two Vims.
2115 * "VimClipboard" was used for previous versions, using the first
2116 * character to specify MCHAR, MLINE or MBLOCK.
2117 */
2118 clip_star.format = RegisterClipboardFormat("VimClipboard2");
2119 clip_star.format_raw = RegisterClipboardFormat("VimRawBytes");
2120#endif
2121
2122 /* This will be NULL on anything but NT 4.0 */
2123 s_pfnGetConsoleKeyboardLayoutName =
2124 (PFNGCKLN) GetProcAddress(GetModuleHandle("kernel32.dll"),
2125 "GetConsoleKeyboardLayoutNameA");
2126}
2127
2128/*
2129 * non-GUI version of mch_exit().
2130 * Shut down and exit with status `r'
2131 * Careful: mch_exit() may be called before mch_init()!
2132 */
2133 void
2134mch_exit(int r)
2135{
2136 stoptermcap();
2137
2138 if (g_fWindInitCalled)
2139 settmode(TMODE_COOK);
2140
2141 ml_close_all(TRUE); /* remove all memfiles */
2142
2143 if (g_fWindInitCalled)
2144 {
2145#ifdef FEAT_TITLE
2146 mch_restore_title(3);
2147 /*
2148 * Restore both the small and big icons of the console window to
2149 * what they were at startup. Don't do this when the window is
2150 * closed, Vim would hang here.
2151 */
2152 if (g_fCanChangeIcon && !g_fForceExit)
2153 SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon);
2154#endif
2155
2156#ifdef MCH_WRITE_DUMP
2157 if (fdDump)
2158 {
2159 time_t t;
2160
2161 time(&t);
2162 fputs(ctime(&t), fdDump);
2163 fclose(fdDump);
2164 }
2165 fdDump = NULL;
2166#endif
2167 }
2168
2169 SetConsoleCursorInfo(g_hConOut, &g_cci);
2170 SetConsoleMode(g_hConIn, g_cmodein);
2171 SetConsoleMode(g_hConOut, g_cmodeout);
2172
2173#ifdef DYNAMIC_GETTEXT
2174 dyn_libintl_end();
2175#endif
2176
2177 exit(r);
2178}
2179#endif /* !FEAT_GUI_W32 */
2180
2181
2182/*
2183 * Do we have an interactive window?
2184 */
2185 int
2186mch_check_win(
2187 int argc,
2188 char **argv)
2189{
2190 get_exe_name();
2191
2192#ifdef FEAT_GUI_W32
2193 return OK; /* GUI always has a tty */
2194#else
2195 if (isatty(1))
2196 return OK;
2197 return FAIL;
2198#endif
2199}
2200
2201
2202/*
2203 * fname_case(): Set the case of the file name, if it already exists.
2204 * When "len" is > 0, also expand short to long filenames.
2205 */
2206 void
2207fname_case(
2208 char_u *name,
2209 int len)
2210{
2211 char szTrueName[_MAX_PATH + 2];
2212 char *ptrue, *ptruePrev;
2213 char *porig, *porigPrev;
2214 int flen;
2215 WIN32_FIND_DATA fb;
2216 HANDLE hFind;
2217 int c;
2218
2219 flen = (name != NULL) ? (int)STRLEN(name) : 0;
2220 if (flen == 0 || flen > _MAX_PATH)
2221 return;
2222
2223 slash_adjust(name);
2224
2225 /* Build the new name in szTrueName[] one component at a time. */
2226 porig = name;
2227 ptrue = szTrueName;
2228
2229 if (isalpha(porig[0]) && porig[1] == ':')
2230 {
2231 /* copy leading drive letter */
2232 *ptrue++ = *porig++;
2233 *ptrue++ = *porig++;
2234 *ptrue = NUL; /* in case nothing follows */
2235 }
2236
2237 while (*porig != NUL)
2238 {
2239 /* copy \ characters */
2240 while (*porig == psepc)
2241 *ptrue++ = *porig++;
2242
2243 ptruePrev = ptrue;
2244 porigPrev = porig;
2245 while (*porig != NUL && *porig != psepc)
2246 {
2247#ifdef FEAT_MBYTE
2248 int l;
2249
2250 if (enc_dbcs)
2251 {
2252 l = (*mb_ptr2len_check)(porig);
2253 while (--l >= 0)
2254 *ptrue++ = *porig++;
2255 }
2256 else
2257#endif
2258 *ptrue++ = *porig++;
2259 }
2260 *ptrue = NUL;
2261
2262 /* Skip "", "." and "..". */
2263 if (ptrue > ptruePrev
2264 && (ptruePrev[0] != '.'
2265 || (ptruePrev[1] != NUL
2266 && (ptruePrev[1] != '.' || ptruePrev[2] != NUL)))
2267 && (hFind = FindFirstFile(szTrueName, &fb))
2268 != INVALID_HANDLE_VALUE)
2269 {
2270 c = *porig;
2271 *porig = NUL;
2272
2273 /* Only use the match when it's the same name (ignoring case) or
2274 * expansion is allowed and there is a match with the short name
2275 * and there is enough room. */
2276 if (_stricoll(porigPrev, fb.cFileName) == 0
2277 || (len > 0
2278 && (_stricoll(porigPrev, fb.cAlternateFileName) == 0
2279 && (int)(ptruePrev - szTrueName)
2280 + (int)strlen(fb.cFileName) < len)))
2281 {
2282 STRCPY(ptruePrev, fb.cFileName);
2283
2284 /* Look for exact match and prefer it if found. Must be a
2285 * long name, otherwise there would be only one match. */
2286 while (FindNextFile(hFind, &fb))
2287 {
2288 if (*fb.cAlternateFileName != NUL
2289 && (strcoll(porigPrev, fb.cFileName) == 0
2290 || (len > 0
2291 && (_stricoll(porigPrev,
2292 fb.cAlternateFileName) == 0
2293 && (int)(ptruePrev - szTrueName)
2294 + (int)strlen(fb.cFileName) < len))))
2295 {
2296 STRCPY(ptruePrev, fb.cFileName);
2297 break;
2298 }
2299 }
2300 }
2301 FindClose(hFind);
2302 *porig = c;
2303 ptrue = ptruePrev + strlen(ptruePrev);
2304 }
2305 }
2306
2307 STRCPY(name, szTrueName);
2308}
2309
2310
2311/*
2312 * Insert user name in s[len].
2313 */
2314 int
2315mch_get_user_name(
2316 char_u *s,
2317 int len)
2318{
2319 char szUserName[MAX_COMPUTERNAME_LENGTH + 1];
2320 DWORD cch = sizeof szUserName;
2321
2322 if (GetUserName(szUserName, &cch))
2323 {
2324 STRNCPY(s, szUserName, len);
2325 return OK;
2326 }
2327 s[0] = NUL;
2328 return FAIL;
2329}
2330
2331
2332/*
2333 * Insert host name in s[len].
2334 */
2335 void
2336mch_get_host_name(
2337 char_u *s,
2338 int len)
2339{
2340 DWORD cch = len;
2341
2342 if (!GetComputerName(s, &cch))
2343 {
2344 STRNCPY(s, "PC (Win32 Vim)", len);
2345 s[len - 1] = NUL; /* make sure it's terminated */
2346 }
2347}
2348
2349
2350/*
2351 * return process ID
2352 */
2353 long
2354mch_get_pid()
2355{
2356 return (long)GetCurrentProcessId();
2357}
2358
2359
2360/*
2361 * Get name of current directory into buffer 'buf' of length 'len' bytes.
2362 * Return OK for success, FAIL for failure.
2363 */
2364 int
2365mch_dirname(
2366 char_u *buf,
2367 int len)
2368{
2369 /*
2370 * Originally this was:
2371 * return (getcwd(buf, len) != NULL ? OK : FAIL);
2372 * But the Win32s known bug list says that getcwd() doesn't work
2373 * so use the Win32 system call instead. <Negri>
2374 */
2375#ifdef FEAT_MBYTE
2376 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2377 {
2378 WCHAR wbuf[_MAX_PATH + 1];
2379
2380 if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0)
2381 {
2382 char_u *p = ucs2_to_enc(wbuf, NULL);
2383
2384 if (p != NULL)
2385 {
2386 STRNCPY(buf, p, len - 1);
2387 buf[len - 1] = NUL;
2388 vim_free(p);
2389 return OK;
2390 }
2391 }
2392 /* Retry with non-wide function (for Windows 98). */
2393 }
2394#endif
2395 return (GetCurrentDirectory(len, buf) != 0 ? OK : FAIL);
2396}
2397
2398/*
2399 * get file permissions for `name'
2400 * -1 : error
2401 * else FILE_ATTRIBUTE_* defined in winnt.h
2402 */
2403 long
2404mch_getperm(
2405 char_u *name)
2406{
2407#ifdef FEAT_MBYTE
2408 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2409 {
2410 WCHAR *p = enc_to_ucs2(name, NULL);
2411 long n;
2412
2413 if (p != NULL)
2414 {
2415 n = (long)GetFileAttributesW(p);
2416 vim_free(p);
2417 if (n >= 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
2418 return n;
2419 /* Retry with non-wide function (for Windows 98). */
2420 }
2421 }
2422#endif
2423 return (long)GetFileAttributes((char *)name);
2424}
2425
2426
2427/*
2428 * set file permission for `name' to `perm'
2429 */
2430 int
2431mch_setperm(
2432 char_u *name,
2433 long perm)
2434{
2435 perm |= FILE_ATTRIBUTE_ARCHIVE; /* file has changed, set archive bit */
2436#ifdef FEAT_MBYTE
2437 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2438 {
2439 WCHAR *p = enc_to_ucs2(name, NULL);
2440 long n;
2441
2442 if (p != NULL)
2443 {
2444 n = (long)SetFileAttributesW(p, perm);
2445 vim_free(p);
2446 if (n || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
2447 return n ? OK : FAIL;
2448 /* Retry with non-wide function (for Windows 98). */
2449 }
2450 }
2451#endif
2452 return SetFileAttributes((char *)name, perm) ? OK : FAIL;
2453}
2454
2455/*
2456 * Set hidden flag for "name".
2457 */
2458 void
2459mch_hide(char_u *name)
2460{
2461 int perm;
2462#ifdef FEAT_MBYTE
2463 WCHAR *p = NULL;
2464
2465 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2466 p = enc_to_ucs2(name, NULL);
2467#endif
2468
2469#ifdef FEAT_MBYTE
2470 if (p != NULL)
2471 {
2472 perm = GetFileAttributesW(p);
2473 if (perm < 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2474 {
2475 /* Retry with non-wide function (for Windows 98). */
2476 vim_free(p);
2477 p = NULL;
2478 }
2479 }
2480 if (p == NULL)
2481#endif
2482 perm = GetFileAttributes((char *)name);
2483 if (perm >= 0)
2484 {
2485 perm |= FILE_ATTRIBUTE_HIDDEN;
2486#ifdef FEAT_MBYTE
2487 if (p != NULL)
2488 {
2489 if (SetFileAttributesW(p, perm) == 0
2490 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2491 {
2492 /* Retry with non-wide function (for Windows 98). */
2493 vim_free(p);
2494 p = NULL;
2495 }
2496 }
2497 if (p == NULL)
2498#endif
2499 SetFileAttributes((char *)name, perm);
2500 }
2501#ifdef FEAT_MBYTE
2502 vim_free(p);
2503#endif
2504}
2505
2506/*
2507 * return TRUE if "name" is a directory
2508 * return FALSE if "name" is not a directory or upon error
2509 */
2510 int
2511mch_isdir(char_u *name)
2512{
2513 int f = mch_getperm(name);
2514
2515 if (f == -1)
2516 return FALSE; /* file does not exist at all */
2517
2518 return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
2519}
2520
2521/*
2522 * Return TRUE if file or directory "name" is writable (not readonly).
2523 * Strange semantics of Win32: a readonly directory is writable, but you can't
2524 * delete a file. Let's say this means it is writable.
2525 */
2526 int
2527mch_writable(char_u *name)
2528{
2529 int perm = mch_getperm(name);
2530
2531 return (perm != -1 && (!(perm & FILE_ATTRIBUTE_READONLY)
2532 || (perm & FILE_ATTRIBUTE_DIRECTORY)));
2533}
2534
2535#if defined(FEAT_EVAL) || defined(PROTO)
2536/*
2537 * Return 1 if "name" can be executed, 0 if not.
2538 * Return -1 if unknown.
2539 */
2540 int
2541mch_can_exe(char_u *name)
2542{
2543 return executable_exists((char *)name);
2544}
2545#endif
2546
2547/*
2548 * Check what "name" is:
2549 * NODE_NORMAL: file or directory (or doesn't exist)
2550 * NODE_WRITABLE: writable device, socket, fifo, etc.
2551 * NODE_OTHER: non-writable things
2552 */
2553 int
2554mch_nodetype(char_u *name)
2555{
2556 HANDLE hFile;
2557 int type;
2558
2559 hFile = CreateFile(name, /* file name */
2560 GENERIC_WRITE, /* access mode */
2561 0, /* share mode */
2562 NULL, /* security descriptor */
2563 OPEN_EXISTING, /* creation disposition */
2564 0, /* file attributes */
2565 NULL); /* handle to template file */
2566
2567 if (hFile == INVALID_HANDLE_VALUE)
2568 return NODE_NORMAL;
2569
2570 type = GetFileType(hFile);
2571 CloseHandle(hFile);
2572 if (type == FILE_TYPE_CHAR)
2573 return NODE_WRITABLE;
2574 if (type == FILE_TYPE_DISK)
2575 return NODE_NORMAL;
2576 return NODE_OTHER;
2577}
2578
2579#ifdef HAVE_ACL
2580struct my_acl
2581{
2582 PSECURITY_DESCRIPTOR pSecurityDescriptor;
2583 PSID pSidOwner;
2584 PSID pSidGroup;
2585 PACL pDacl;
2586 PACL pSacl;
2587};
2588#endif
2589
2590/*
2591 * Return a pointer to the ACL of file "fname" in allocated memory.
2592 * Return NULL if the ACL is not available for whatever reason.
2593 */
2594 vim_acl_T
2595mch_get_acl(fname)
2596 char_u *fname;
2597{
2598#ifndef HAVE_ACL
2599 return (vim_acl_T)NULL;
2600#else
2601 struct my_acl *p = NULL;
2602
2603 /* This only works on Windows NT and 2000. */
2604 if (g_PlatformId == VER_PLATFORM_WIN32_NT && advapi_lib != NULL)
2605 {
2606 p = (struct my_acl *)alloc_clear((unsigned)sizeof(struct my_acl));
2607 if (p != NULL)
2608 {
2609 if (pGetNamedSecurityInfo(
2610 (LPTSTR)fname, // Abstract filename
2611 SE_FILE_OBJECT, // File Object
2612 // Retrieve the entire security descriptor.
2613 OWNER_SECURITY_INFORMATION |
2614 GROUP_SECURITY_INFORMATION |
2615 DACL_SECURITY_INFORMATION |
2616 SACL_SECURITY_INFORMATION,
2617 &p->pSidOwner, // Ownership information.
2618 &p->pSidGroup, // Group membership.
2619 &p->pDacl, // Discretionary information.
2620 &p->pSacl, // For auditing purposes.
2621 &p->pSecurityDescriptor
2622 ) != ERROR_SUCCESS)
2623 {
2624 mch_free_acl((vim_acl_T)p);
2625 p = NULL;
2626 }
2627 }
2628 }
2629
2630 return (vim_acl_T)p;
2631#endif
2632}
2633
2634/*
2635 * Set the ACL of file "fname" to "acl" (unless it's NULL).
2636 * Errors are ignored.
2637 * This must only be called with "acl" equal to what mch_get_acl() returned.
2638 */
2639 void
2640mch_set_acl(fname, acl)
2641 char_u *fname;
2642 vim_acl_T acl;
2643{
2644#ifdef HAVE_ACL
2645 struct my_acl *p = (struct my_acl *)acl;
2646
2647 if (p != NULL && advapi_lib != NULL)
2648 (void)pSetNamedSecurityInfo(
2649 (LPTSTR)fname, // Abstract filename
2650 SE_FILE_OBJECT, // File Object
2651 // Retrieve the entire security descriptor.
2652 OWNER_SECURITY_INFORMATION |
2653 GROUP_SECURITY_INFORMATION |
2654 DACL_SECURITY_INFORMATION |
2655 SACL_SECURITY_INFORMATION,
2656 p->pSidOwner, // Ownership information.
2657 p->pSidGroup, // Group membership.
2658 p->pDacl, // Discretionary information.
2659 p->pSacl // For auditing purposes.
2660 );
2661#endif
2662}
2663
2664 void
2665mch_free_acl(acl)
2666 vim_acl_T acl;
2667{
2668#ifdef HAVE_ACL
2669 struct my_acl *p = (struct my_acl *)acl;
2670
2671 if (p != NULL)
2672 {
2673 LocalFree(p->pSecurityDescriptor); // Free the memory just in case
2674 vim_free(p);
2675 }
2676#endif
2677}
2678
2679#ifndef FEAT_GUI_W32
2680
2681/*
2682 * handler for ctrl-break, ctrl-c interrupts, and fatal events.
2683 */
2684 static BOOL WINAPI
2685handler_routine(
2686 DWORD dwCtrlType)
2687{
2688 switch (dwCtrlType)
2689 {
2690 case CTRL_C_EVENT:
2691 if (ctrl_c_interrupts)
2692 g_fCtrlCPressed = TRUE;
2693 return TRUE;
2694
2695 case CTRL_BREAK_EVENT:
2696 g_fCBrkPressed = TRUE;
2697 return TRUE;
2698
2699 /* fatal events: shut down gracefully */
2700 case CTRL_CLOSE_EVENT:
2701 case CTRL_LOGOFF_EVENT:
2702 case CTRL_SHUTDOWN_EVENT:
2703 windgoto((int)Rows - 1, 0);
2704 g_fForceExit = TRUE;
2705
2706 sprintf((char *)IObuff, _("Vim: Caught %s event\n"),
2707 (dwCtrlType == CTRL_CLOSE_EVENT
2708 ? _("close")
2709 : dwCtrlType == CTRL_LOGOFF_EVENT
2710 ? _("logoff")
2711 : _("shutdown")));
2712#ifdef DEBUG
2713 OutputDebugString(IObuff);
2714#endif
2715
2716 preserve_exit(); /* output IObuff, preserve files and exit */
2717
2718 return TRUE; /* not reached */
2719
2720 default:
2721 return FALSE;
2722 }
2723}
2724
2725
2726/*
2727 * set the tty in (raw) ? "raw" : "cooked" mode
2728 */
2729 void
2730mch_settmode(
2731 int tmode)
2732{
2733 DWORD cmodein;
2734 DWORD cmodeout;
2735 BOOL bEnableHandler;
2736
2737 GetConsoleMode(g_hConIn, &cmodein);
2738 GetConsoleMode(g_hConOut, &cmodeout);
2739 if (tmode == TMODE_RAW)
2740 {
2741 cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
2742 ENABLE_ECHO_INPUT);
2743#ifdef FEAT_MOUSE
2744 if (g_fMouseActive)
2745 cmodein |= ENABLE_MOUSE_INPUT;
2746#endif
2747 cmodeout &= ~(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
2748 bEnableHandler = TRUE;
2749 }
2750 else /* cooked */
2751 {
2752 cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
2753 ENABLE_ECHO_INPUT);
2754 cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
2755 bEnableHandler = FALSE;
2756 }
2757 SetConsoleMode(g_hConIn, cmodein);
2758 SetConsoleMode(g_hConOut, cmodeout);
2759 SetConsoleCtrlHandler(handler_routine, bEnableHandler);
2760
2761#ifdef MCH_WRITE_DUMP
2762 if (fdDump)
2763 {
2764 fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n",
2765 tmode == TMODE_RAW ? "raw" :
2766 tmode == TMODE_COOK ? "cooked" : "normal",
2767 cmodein, cmodeout);
2768 fflush(fdDump);
2769 }
2770#endif
2771}
2772
2773
2774/*
2775 * Get the size of the current window in `Rows' and `Columns'
2776 * Return OK when size could be determined, FAIL otherwise.
2777 */
2778 int
2779mch_get_shellsize()
2780{
2781 CONSOLE_SCREEN_BUFFER_INFO csbi;
2782
2783 if (!g_fTermcapMode && g_cbTermcap.IsValid)
2784 {
2785 /*
2786 * For some reason, we are trying to get the screen dimensions
2787 * even though we are not in termcap mode. The 'Rows' and 'Columns'
2788 * variables are really intended to mean the size of Vim screen
2789 * while in termcap mode.
2790 */
2791 Rows = g_cbTermcap.Info.dwSize.Y;
2792 Columns = g_cbTermcap.Info.dwSize.X;
2793 }
2794 else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2795 {
2796 Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
2797 Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
2798 }
2799 else
2800 {
2801 Rows = 25;
2802 Columns = 80;
2803 }
2804 return OK;
2805}
2806
2807/*
2808 * Set a console window to `xSize' * `ySize'
2809 */
2810 static void
2811ResizeConBufAndWindow(
2812 HANDLE hConsole,
2813 int xSize,
2814 int ySize)
2815{
2816 CONSOLE_SCREEN_BUFFER_INFO csbi; /* hold current console buffer info */
2817 SMALL_RECT srWindowRect; /* hold the new console size */
2818 COORD coordScreen;
2819
2820#ifdef MCH_WRITE_DUMP
2821 if (fdDump)
2822 {
2823 fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
2824 fflush(fdDump);
2825 }
2826#endif
2827
2828 /* get the largest size we can size the console window to */
2829 coordScreen = GetLargestConsoleWindowSize(hConsole);
2830
2831 /* define the new console window size and scroll position */
2832 srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
2833 srWindowRect.Right = (SHORT) (min(xSize, coordScreen.X) - 1);
2834 srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
2835
2836 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2837 {
2838 int sx, sy;
2839
2840 sx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
2841 sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
2842 if (sy < ySize || sx < xSize)
2843 {
2844 /*
2845 * Increasing number of lines/columns, do buffer first.
2846 * Use the maximal size in x and y direction.
2847 */
2848 if (sy < ySize)
2849 coordScreen.Y = ySize;
2850 else
2851 coordScreen.Y = sy;
2852 if (sx < xSize)
2853 coordScreen.X = xSize;
2854 else
2855 coordScreen.X = sx;
2856 SetConsoleScreenBufferSize(hConsole, coordScreen);
2857 }
2858 }
2859
2860 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &srWindowRect))
2861 {
2862#ifdef MCH_WRITE_DUMP
2863 if (fdDump)
2864 {
2865 fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
2866 GetLastError());
2867 fflush(fdDump);
2868 }
2869#endif
2870 }
2871
2872 /* define the new console buffer size */
2873 coordScreen.X = xSize;
2874 coordScreen.Y = ySize;
2875
2876 if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
2877 {
2878#ifdef MCH_WRITE_DUMP
2879 if (fdDump)
2880 {
2881 fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
2882 GetLastError());
2883 fflush(fdDump);
2884 }
2885#endif
2886 }
2887}
2888
2889
2890/*
2891 * Set the console window to `Rows' * `Columns'
2892 */
2893 void
2894mch_set_shellsize()
2895{
2896 COORD coordScreen;
2897
2898 /* Don't change window size while still starting up */
2899 if (suppress_winsize != 0)
2900 {
2901 suppress_winsize = 2;
2902 return;
2903 }
2904
2905 if (term_console)
2906 {
2907 coordScreen = GetLargestConsoleWindowSize(g_hConOut);
2908
2909 /* Clamp Rows and Columns to reasonable values */
2910 if (Rows > coordScreen.Y)
2911 Rows = coordScreen.Y;
2912 if (Columns > coordScreen.X)
2913 Columns = coordScreen.X;
2914
2915 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
2916 }
2917}
2918
2919/*
2920 * Rows and/or Columns has changed.
2921 */
2922 void
2923mch_new_shellsize()
2924{
2925 set_scroll_region(0, 0, Columns - 1, Rows - 1);
2926}
2927
2928
2929/*
2930 * Called when started up, to set the winsize that was delayed.
2931 */
2932 void
2933mch_set_winsize_now()
2934{
2935 if (suppress_winsize == 2)
2936 {
2937 suppress_winsize = 0;
2938 mch_set_shellsize();
2939 shell_resized();
2940 }
2941 suppress_winsize = 0;
2942}
2943#endif /* FEAT_GUI_W32 */
2944
2945
2946
2947#if defined(FEAT_GUI_W32) || defined(PROTO)
2948
2949/*
2950 * Specialised version of system() for Win32 GUI mode.
2951 * This version proceeds as follows:
2952 * 1. Create a console window for use by the subprocess
2953 * 2. Run the subprocess (it gets the allocated console by default)
2954 * 3. Wait for the subprocess to terminate and get its exit code
2955 * 4. Prompt the user to press a key to close the console window
2956 */
2957 static int
2958mch_system(char *cmd, int options)
2959{
2960 STARTUPINFO si;
2961 PROCESS_INFORMATION pi;
2962 DWORD ret = 0;
2963 HWND hwnd = GetFocus();
2964
2965 si.cb = sizeof(si);
2966 si.lpReserved = NULL;
2967 si.lpDesktop = NULL;
2968 si.lpTitle = NULL;
2969 si.dwFlags = STARTF_USESHOWWINDOW;
2970 /*
2971 * It's nicer to run a filter command in a minimized window, but in
2972 * Windows 95 this makes the command MUCH slower. We can't do it under
2973 * Win32s either as it stops the synchronous spawn workaround working.
2974 */
2975 if ((options & SHELL_DOOUT) && !mch_windows95() && !gui_is_win32s())
2976 si.wShowWindow = SW_SHOWMINIMIZED;
2977 else
2978 si.wShowWindow = SW_SHOWNORMAL;
2979 si.cbReserved2 = 0;
2980 si.lpReserved2 = NULL;
2981
2982 /* There is a strange error on Windows 95 when using "c:\\command.com".
2983 * When the "c:\\" is left out it works OK...? */
2984 if (mch_windows95()
2985 && (STRNICMP(cmd, "c:/command.com", 14) == 0
2986 || STRNICMP(cmd, "c:\\command.com", 14) == 0))
2987 cmd += 3;
2988
2989 /* Now, run the command */
2990 CreateProcess(NULL, /* Executable name */
2991 cmd, /* Command to execute */
2992 NULL, /* Process security attributes */
2993 NULL, /* Thread security attributes */
2994 FALSE, /* Inherit handles */
2995 CREATE_DEFAULT_ERROR_MODE | /* Creation flags */
2996 CREATE_NEW_CONSOLE,
2997 NULL, /* Environment */
2998 NULL, /* Current directory */
2999 &si, /* Startup information */
3000 &pi); /* Process information */
3001
3002
3003 /* Wait for the command to terminate before continuing */
3004 if (g_PlatformId != VER_PLATFORM_WIN32s)
3005 {
3006#ifdef FEAT_GUI
3007 int delay = 1;
3008
3009 /* Keep updating the window while waiting for the shell to finish. */
3010 for (;;)
3011 {
3012 MSG msg;
3013
3014 if (PeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
3015 {
3016 TranslateMessage(&msg);
3017 DispatchMessage(&msg);
3018 }
3019 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
3020 break;
3021
3022 /* We start waiting for a very short time and then increase it, so
3023 * that we respond quickly when the process is quick, and don't
3024 * consume too much overhead when it's slow. */
3025 if (delay < 50)
3026 delay += 10;
3027 }
3028#else
3029 WaitForSingleObject(pi.hProcess, INFINITE);
3030#endif
3031
3032 /* Get the command exit code */
3033 GetExitCodeProcess(pi.hProcess, &ret);
3034 }
3035 else
3036 {
3037 /*
3038 * This ugly code is the only quick way of performing
3039 * a synchronous spawn under Win32s. Yuk.
3040 */
3041 num_windows = 0;
3042 EnumWindows(win32ssynch_cb, 0);
3043 old_num_windows = num_windows;
3044 do
3045 {
3046 Sleep(1000);
3047 num_windows = 0;
3048 EnumWindows(win32ssynch_cb, 0);
3049 } while (num_windows == old_num_windows);
3050 ret = 0;
3051 }
3052
3053 /* Close the handles to the subprocess, so that it goes away */
3054 CloseHandle(pi.hThread);
3055 CloseHandle(pi.hProcess);
3056
3057 /* Try to get input focus back. Doesn't always work though. */
3058 PostMessage(hwnd, WM_SETFOCUS, 0, 0);
3059
3060 return ret;
3061}
3062#else
3063
3064# define mch_system(c, o) system(c)
3065
3066#endif
3067
3068/*
3069 * Either execute a command by calling the shell or start a new shell
3070 */
3071 int
3072mch_call_shell(
3073 char_u *cmd,
3074 int options) /* SHELL_*, see vim.h */
3075{
3076 int x = 0;
3077 int tmode = cur_tmode;
3078#ifdef FEAT_TITLE
3079 char szShellTitle[512];
3080
3081 /* Change the title to reflect that we are in a subshell. */
3082 if (GetConsoleTitle(szShellTitle, sizeof(szShellTitle) - 4) > 0)
3083 {
3084 if (cmd == NULL)
3085 strcat(szShellTitle, " :sh");
3086 else
3087 {
3088 strcat(szShellTitle, " - !");
3089 if ((strlen(szShellTitle) + strlen(cmd) < sizeof(szShellTitle)))
3090 strcat(szShellTitle, cmd);
3091 }
3092 mch_settitle(szShellTitle, NULL);
3093 }
3094#endif
3095
3096 out_flush();
3097
3098#ifdef MCH_WRITE_DUMP
3099 if (fdDump)
3100 {
3101 fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
3102 fflush(fdDump);
3103 }
3104#endif
3105
3106 /*
3107 * Catch all deadly signals while running the external command, because a
3108 * CTRL-C, Ctrl-Break or illegal instruction might otherwise kill us.
3109 */
3110 signal(SIGINT, SIG_IGN);
3111#if defined(__GNUC__) && !defined(__MINGW32__)
3112 signal(SIGKILL, SIG_IGN);
3113#else
3114 signal(SIGBREAK, SIG_IGN);
3115#endif
3116 signal(SIGILL, SIG_IGN);
3117 signal(SIGFPE, SIG_IGN);
3118 signal(SIGSEGV, SIG_IGN);
3119 signal(SIGTERM, SIG_IGN);
3120 signal(SIGABRT, SIG_IGN);
3121
3122 if (options & SHELL_COOKED)
3123 settmode(TMODE_COOK); /* set to normal mode */
3124
3125 if (cmd == NULL)
3126 {
3127 x = mch_system(p_sh, options);
3128 }
3129 else
3130 {
3131 /* we use "command" or "cmd" to start the shell; slow but easy */
3132 char_u *newcmd;
3133
3134 newcmd = lalloc((long_u) (
3135#ifdef FEAT_GUI_W32
3136 STRLEN(vimrun_path) +
3137#endif
3138 STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10), TRUE);
3139 if (newcmd != NULL)
3140 {
3141 char_u *cmdbase = (*cmd == '"' ? cmd + 1 : cmd);
3142
3143 if ((STRNICMP(cmdbase, "start", 5) == 0) && vim_iswhite(cmdbase[5]))
3144 {
3145 STARTUPINFO si;
3146 PROCESS_INFORMATION pi;
3147
3148 si.cb = sizeof(si);
3149 si.lpReserved = NULL;
3150 si.lpDesktop = NULL;
3151 si.lpTitle = NULL;
3152 si.dwFlags = 0;
3153 si.cbReserved2 = 0;
3154 si.lpReserved2 = NULL;
3155
3156 cmdbase = skipwhite(cmdbase + 5);
3157 if ((STRNICMP(cmdbase, "/min", 4) == 0)
3158 && vim_iswhite(cmdbase[4]))
3159 {
3160 cmdbase = skipwhite(cmdbase + 4);
3161 si.dwFlags = STARTF_USESHOWWINDOW;
3162 si.wShowWindow = SW_SHOWMINNOACTIVE;
3163 }
3164
3165 /* When the command is in double quotes, but 'shellxquote' is
3166 * empty, keep the double quotes around the command.
3167 * Otherwise remove the double quotes, they aren't needed
3168 * here, because we don't use a shell to run the command. */
3169 if (*cmd == '"' && *p_sxq == NUL)
3170 {
3171 newcmd[0] = '"';
3172 STRCPY(newcmd + 1, cmdbase);
3173 }
3174 else
3175 {
3176 STRCPY(newcmd, cmdbase);
3177 if (*cmd == '"' && *newcmd != NUL)
3178 newcmd[STRLEN(newcmd) - 1] = NUL;
3179 }
3180
3181 /*
3182 * Now, start the command as a process, so that it doesn't
3183 * inherit our handles which causes unpleasant dangling swap
3184 * files if we exit before the spawned process
3185 */
3186 if (CreateProcess (NULL, // Executable name
3187 newcmd, // Command to execute
3188 NULL, // Process security attributes
3189 NULL, // Thread security attributes
3190 FALSE, // Inherit handles
3191 CREATE_NEW_CONSOLE, // Creation flags
3192 NULL, // Environment
3193 NULL, // Current directory
3194 &si, // Startup information
3195 &pi)) // Process information
3196 x = 0;
3197 else
3198 {
3199 x = -1;
3200#ifdef FEAT_GUI_W32
3201 EMSG(_("E371: Command not found"));
3202#endif
3203 }
3204 /* Close the handles to the subprocess, so that it goes away */
3205 CloseHandle(pi.hThread);
3206 CloseHandle(pi.hProcess);
3207 }
3208 else
3209 {
3210#if defined(FEAT_GUI_W32)
3211 if (need_vimrun_warning)
3212 {
3213 MessageBox(NULL,
3214 _("VIMRUN.EXE not found in your $PATH.\n"
3215 "External commands will not pause after completion.\n"
3216 "See :help win32-vimrun for more information."),
3217 _("Vim Warning"),
3218 MB_ICONWARNING);
3219 need_vimrun_warning = FALSE;
3220 }
3221 if (!s_dont_use_vimrun)
3222 /* Use vimrun to execute the command. It opens a console
3223 * window, which can be closed without killing Vim. */
3224 sprintf((char *)newcmd, "%s%s%s %s %s",
3225 vimrun_path,
3226 (msg_silent != 0 || (options & SHELL_DOOUT))
3227 ? "-s " : "",
3228 p_sh, p_shcf, cmd);
3229 else
3230#endif
3231 sprintf((char *)newcmd, "%s %s %s", p_sh, p_shcf, cmd);
3232 x = mch_system((char *)newcmd, options);
3233 }
3234 vim_free(newcmd);
3235 }
3236 }
3237
3238 if (tmode == TMODE_RAW)
3239 settmode(TMODE_RAW); /* set to raw mode */
3240
3241 /* Print the return value, unless "vimrun" was used. */
3242 if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent
3243#if defined(FEAT_GUI_W32)
3244 && ((options & SHELL_DOOUT) || s_dont_use_vimrun)
3245#endif
3246 )
3247 {
3248 smsg(_("shell returned %d"), x);
3249 msg_putchar('\n');
3250 }
3251#ifdef FEAT_TITLE
3252 resettitle();
3253#endif
3254
3255 signal(SIGINT, SIG_DFL);
3256#if defined(__GNUC__) && !defined(__MINGW32__)
3257 signal(SIGKILL, SIG_DFL);
3258#else
3259 signal(SIGBREAK, SIG_DFL);
3260#endif
3261 signal(SIGILL, SIG_DFL);
3262 signal(SIGFPE, SIG_DFL);
3263 signal(SIGSEGV, SIG_DFL);
3264 signal(SIGTERM, SIG_DFL);
3265 signal(SIGABRT, SIG_DFL);
3266
3267 return x;
3268}
3269
3270
3271#ifndef FEAT_GUI_W32
3272
3273/*
3274 * Start termcap mode
3275 */
3276 static void
3277termcap_mode_start(void)
3278{
3279 DWORD cmodein;
3280
3281 if (g_fTermcapMode)
3282 return;
3283
3284 SaveConsoleBuffer(&g_cbNonTermcap);
3285
3286 if (g_cbTermcap.IsValid)
3287 {
3288 /*
3289 * We've been in termcap mode before. Restore certain screen
3290 * characteristics, including the buffer size and the window
3291 * size. Since we will be redrawing the screen, we don't need
3292 * to restore the actual contents of the buffer.
3293 */
3294 RestoreConsoleBuffer(&g_cbTermcap, FALSE);
3295 SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow);
3296 Rows = g_cbTermcap.Info.dwSize.Y;
3297 Columns = g_cbTermcap.Info.dwSize.X;
3298 }
3299 else
3300 {
3301 /*
3302 * This is our first time entering termcap mode. Clear the console
3303 * screen buffer, and resize the buffer to match the current window
3304 * size. We will use this as the size of our editing environment.
3305 */
3306 ClearConsoleBuffer(g_attrCurrent);
3307 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
3308 }
3309
3310#ifdef FEAT_TITLE
3311 resettitle();
3312#endif
3313
3314 GetConsoleMode(g_hConIn, &cmodein);
3315#ifdef FEAT_MOUSE
3316 if (g_fMouseActive)
3317 cmodein |= ENABLE_MOUSE_INPUT;
3318 else
3319 cmodein &= ~ENABLE_MOUSE_INPUT;
3320#endif
3321 cmodein |= ENABLE_WINDOW_INPUT;
3322 SetConsoleMode(g_hConIn, cmodein);
3323
3324 redraw_later_clear();
3325 g_fTermcapMode = TRUE;
3326}
3327
3328
3329/*
3330 * End termcap mode
3331 */
3332 static void
3333termcap_mode_end(void)
3334{
3335 DWORD cmodein;
3336 ConsoleBuffer *cb;
3337 COORD coord;
3338 DWORD dwDummy;
3339
3340 if (!g_fTermcapMode)
3341 return;
3342
3343 SaveConsoleBuffer(&g_cbTermcap);
3344
3345 GetConsoleMode(g_hConIn, &cmodein);
3346 cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
3347 SetConsoleMode(g_hConIn, cmodein);
3348
3349#ifdef FEAT_RESTORE_ORIG_SCREEN
3350 cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
3351#else
3352 cb = &g_cbNonTermcap;
3353#endif
3354 RestoreConsoleBuffer(cb, p_rs);
3355 SetConsoleCursorInfo(g_hConOut, &g_cci);
3356
3357 if (p_rs || exiting)
3358 {
3359 /*
3360 * Clear anything that happens to be on the current line.
3361 */
3362 coord.X = 0;
3363 coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1));
3364 FillConsoleOutputCharacter(g_hConOut, ' ',
3365 cb->Info.dwSize.X, coord, &dwDummy);
3366 /*
3367 * The following is just for aesthetics. If we are exiting without
3368 * restoring the screen, then we want to have a prompt string
3369 * appear at the bottom line. However, the command interpreter
3370 * seems to always advance the cursor one line before displaying
3371 * the prompt string, which causes the screen to scroll. To
3372 * counter this, move the cursor up one line before exiting.
3373 */
3374 if (exiting && !p_rs)
3375 coord.Y--;
3376 /*
3377 * Position the cursor at the leftmost column of the desired row.
3378 */
3379 SetConsoleCursorPosition(g_hConOut, coord);
3380 }
3381
3382 g_fTermcapMode = FALSE;
3383}
3384#endif /* FEAT_GUI_W32 */
3385
3386
3387#ifdef FEAT_GUI_W32
3388 void
3389mch_write(
3390 char_u *s,
3391 int len)
3392{
3393 /* never used */
3394}
3395
3396#else
3397
3398/*
3399 * clear `n' chars, starting from `coord'
3400 */
3401 static void
3402clear_chars(
3403 COORD coord,
3404 DWORD n)
3405{
3406 DWORD dwDummy;
3407
3408 FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
3409 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy);
3410}
3411
3412
3413/*
3414 * Clear the screen
3415 */
3416 static void
3417clear_screen(void)
3418{
3419 g_coord.X = g_coord.Y = 0;
3420 clear_chars(g_coord, Rows * Columns);
3421}
3422
3423
3424/*
3425 * Clear to end of display
3426 */
3427 static void
3428clear_to_end_of_display(void)
3429{
3430 clear_chars(g_coord, (Rows - g_coord.Y - 1)
3431 * Columns + (Columns - g_coord.X));
3432}
3433
3434
3435/*
3436 * Clear to end of line
3437 */
3438 static void
3439clear_to_end_of_line(void)
3440{
3441 clear_chars(g_coord, Columns - g_coord.X);
3442}
3443
3444
3445/*
3446 * Scroll the scroll region up by `cLines' lines
3447 */
3448 static void
3449scroll(
3450 unsigned cLines)
3451{
3452 COORD oldcoord = g_coord;
3453
3454 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
3455 delete_lines(cLines);
3456
3457 g_coord = oldcoord;
3458}
3459
3460
3461/*
3462 * Set the scroll region
3463 */
3464 static void
3465set_scroll_region(
3466 unsigned left,
3467 unsigned top,
3468 unsigned right,
3469 unsigned bottom)
3470{
3471 if (left >= right
3472 || top >= bottom
3473 || right > (unsigned) Columns - 1
3474 || bottom > (unsigned) Rows - 1)
3475 return;
3476
3477 g_srScrollRegion.Left = left;
3478 g_srScrollRegion.Top = top;
3479 g_srScrollRegion.Right = right;
3480 g_srScrollRegion.Bottom = bottom;
3481}
3482
3483
3484/*
3485 * Insert `cLines' lines at the current cursor position
3486 */
3487 static void
3488insert_lines(
3489 unsigned cLines)
3490{
3491 SMALL_RECT source;
3492 COORD dest;
3493 CHAR_INFO fill;
3494
3495 dest.X = 0;
3496 dest.Y = g_coord.Y + cLines;
3497
3498 source.Left = 0;
3499 source.Top = g_coord.Y;
3500 source.Right = g_srScrollRegion.Right;
3501 source.Bottom = g_srScrollRegion.Bottom - cLines;
3502
3503 fill.Char.AsciiChar = ' ';
3504 fill.Attributes = g_attrCurrent;
3505
3506 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
3507
3508 /* Here we have to deal with a win32 console flake: If the scroll
3509 * region looks like abc and we scroll c to a and fill with d we get
3510 * cbd... if we scroll block c one line at a time to a, we get cdd...
3511 * vim expects cdd consistently... So we have to deal with that
3512 * here... (this also occurs scrolling the same way in the other
3513 * direction). */
3514
3515 if (source.Bottom < dest.Y)
3516 {
3517 COORD coord;
3518
3519 coord.X = 0;
3520 coord.Y = source.Bottom;
3521 clear_chars(coord, Columns * (dest.Y - source.Bottom));
3522 }
3523}
3524
3525
3526/*
3527 * Delete `cLines' lines at the current cursor position
3528 */
3529 static void
3530delete_lines(
3531 unsigned cLines)
3532{
3533 SMALL_RECT source;
3534 COORD dest;
3535 CHAR_INFO fill;
3536 int nb;
3537
3538 dest.X = 0;
3539 dest.Y = g_coord.Y;
3540
3541 source.Left = 0;
3542 source.Top = g_coord.Y + cLines;
3543 source.Right = g_srScrollRegion.Right;
3544 source.Bottom = g_srScrollRegion.Bottom;
3545
3546 fill.Char.AsciiChar = ' ';
3547 fill.Attributes = g_attrCurrent;
3548
3549 ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill);
3550
3551 /* Here we have to deal with a win32 console flake: If the scroll
3552 * region looks like abc and we scroll c to a and fill with d we get
3553 * cbd... if we scroll block c one line at a time to a, we get cdd...
3554 * vim expects cdd consistently... So we have to deal with that
3555 * here... (this also occurs scrolling the same way in the other
3556 * direction). */
3557
3558 nb = dest.Y + (source.Bottom - source.Top) + 1;
3559
3560 if (nb < source.Top)
3561 {
3562 COORD coord;
3563
3564 coord.X = 0;
3565 coord.Y = nb;
3566 clear_chars(coord, Columns * (source.Top - nb));
3567 }
3568}
3569
3570
3571/*
3572 * Set the cursor position
3573 */
3574 static void
3575gotoxy(
3576 unsigned x,
3577 unsigned y)
3578{
3579 if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows)
3580 return;
3581
3582 /* external cursor coords are 1-based; internal are 0-based */
3583 g_coord.X = x - 1;
3584 g_coord.Y = y - 1;
3585 SetConsoleCursorPosition(g_hConOut, g_coord);
3586}
3587
3588
3589/*
3590 * Set the current text attribute = (foreground | background)
3591 * See ../doc/os_win32.txt for the numbers.
3592 */
3593 static void
3594textattr(
3595 WORD wAttr)
3596{
3597 g_attrCurrent = wAttr;
3598
3599 SetConsoleTextAttribute(g_hConOut, wAttr);
3600}
3601
3602
3603 static void
3604textcolor(
3605 WORD wAttr)
3606{
3607 g_attrCurrent = (g_attrCurrent & 0xf0) + wAttr;
3608
3609 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
3610}
3611
3612
3613 static void
3614textbackground(
3615 WORD wAttr)
3616{
3617 g_attrCurrent = (g_attrCurrent & 0x0f) + (wAttr << 4);
3618
3619 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
3620}
3621
3622
3623/*
3624 * restore the default text attribute (whatever we started with)
3625 */
3626 static void
3627normvideo()
3628{
3629 textattr(g_attrDefault);
3630}
3631
3632
3633static WORD g_attrPreStandout = 0;
3634
3635/*
3636 * Make the text standout, by brightening it
3637 */
3638 static void
3639standout(void)
3640{
3641 g_attrPreStandout = g_attrCurrent;
3642 textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
3643}
3644
3645
3646/*
3647 * Turn off standout mode
3648 */
3649 static void
3650standend()
3651{
3652 if (g_attrPreStandout)
3653 {
3654 textattr(g_attrPreStandout);
3655 g_attrPreStandout = 0;
3656 }
3657}
3658
3659
3660/*
3661 * Set normal fg/bg color, based on T_ME. Called whem t_me has been set.
3662 */
3663 void
3664mch_set_normal_colors()
3665{
3666 char_u *p;
3667 int n;
3668
3669 cterm_normal_fg_color = (g_attrDefault & 0xf) + 1;
3670 cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1;
3671 if (T_ME[0] == ESC && T_ME[1] == '|')
3672 {
3673 p = T_ME + 2;
3674 n = getdigits(&p);
3675 if (*p == 'm' && n > 0)
3676 {
3677 cterm_normal_fg_color = (n & 0xf) + 1;
3678 cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
3679 }
3680 }
3681}
3682
3683
3684/*
3685 * visual bell: flash the screen
3686 */
3687 static void
3688visual_bell()
3689{
3690 COORD coordOrigin = {0, 0};
3691 WORD attrFlash = ~g_attrCurrent & 0xff;
3692
3693 DWORD dwDummy;
3694 LPWORD oldattrs = (LPWORD)alloc(Rows * Columns * sizeof(WORD));
3695
3696 if (oldattrs == NULL)
3697 return;
3698 ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
3699 coordOrigin, &dwDummy);
3700 FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns,
3701 coordOrigin, &dwDummy);
3702
3703 Sleep(15); /* wait for 15 msec */
3704 WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
3705 coordOrigin, &dwDummy);
3706 vim_free(oldattrs);
3707}
3708
3709
3710/*
3711 * Make the cursor visible or invisible
3712 */
3713 static void
3714cursor_visible(
3715 BOOL fVisible)
3716{
3717 s_cursor_visible = fVisible;
3718#ifdef MCH_CURSOR_SHAPE
3719 mch_update_cursor();
3720#endif
3721}
3722
3723
3724/*
3725 * write `cchToWrite' characters in `pchBuf' to the screen
3726 * Returns the number of characters actually written (at least one).
3727 */
3728 static BOOL
3729write_chars(
3730 LPCSTR pchBuf,
3731 DWORD cchToWrite)
3732{
3733 COORD coord = g_coord;
3734 DWORD written;
3735
3736 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cchToWrite,
3737 coord, &written);
3738 /* When writing fails or didn't write a single character, pretend one
3739 * character was written, otherwise we get stuck. */
3740 if (WriteConsoleOutputCharacter(g_hConOut, pchBuf, cchToWrite,
3741 coord, &written) == 0
3742 || written == 0)
3743 written = 1;
3744
3745 g_coord.X += (SHORT) written;
3746
3747 while (g_coord.X > g_srScrollRegion.Right)
3748 {
3749 g_coord.X -= (SHORT) Columns;
3750 if (g_coord.Y < g_srScrollRegion.Bottom)
3751 g_coord.Y++;
3752 }
3753
3754 gotoxy(g_coord.X + 1, g_coord.Y + 1);
3755
3756 return written;
3757}
3758
3759
3760/*
3761 * mch_write(): write the output buffer to the screen, translating ESC
3762 * sequences into calls to console output routines.
3763 */
3764 void
3765mch_write(
3766 char_u *s,
3767 int len)
3768{
3769 s[len] = NUL;
3770
3771 if (!term_console)
3772 {
3773 write(1, s, (unsigned)len);
3774 return;
3775 }
3776
3777 /* translate ESC | sequences into faked bios calls */
3778 while (len--)
3779 {
3780 /* optimization: use one single write_chars for runs of text,
3781 * rather than once per character It ain't curses, but it helps. */
3782 DWORD prefix = strcspn(s, "\n\r\b\a\033");
3783
3784 if (p_wd)
3785 {
3786 WaitForChar(p_wd);
3787 if (prefix != 0)
3788 prefix = 1;
3789 }
3790
3791 if (prefix != 0)
3792 {
3793 DWORD nWritten;
3794
3795 nWritten = write_chars(s, prefix);
3796#ifdef MCH_WRITE_DUMP
3797 if (fdDump)
3798 {
3799 fputc('>', fdDump);
3800 fwrite(s, sizeof(char_u), nWritten, fdDump);
3801 fputs("<\n", fdDump);
3802 }
3803#endif
3804 len -= (nWritten - 1);
3805 s += nWritten;
3806 }
3807 else if (s[0] == '\n')
3808 {
3809 /* \n, newline: go to the beginning of the next line or scroll */
3810 if (g_coord.Y == g_srScrollRegion.Bottom)
3811 {
3812 scroll(1);
3813 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
3814 }
3815 else
3816 {
3817 gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
3818 }
3819#ifdef MCH_WRITE_DUMP
3820 if (fdDump)
3821 fputs("\\n\n", fdDump);
3822#endif
3823 s++;
3824 }
3825 else if (s[0] == '\r')
3826 {
3827 /* \r, carriage return: go to beginning of line */
3828 gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
3829#ifdef MCH_WRITE_DUMP
3830 if (fdDump)
3831 fputs("\\r\n", fdDump);
3832#endif
3833 s++;
3834 }
3835 else if (s[0] == '\b')
3836 {
3837 /* \b, backspace: move cursor one position left */
3838 if (g_coord.X > g_srScrollRegion.Left)
3839 g_coord.X--;
3840 else if (g_coord.Y > g_srScrollRegion.Top)
3841 {
3842 g_coord.X = g_srScrollRegion.Right;
3843 g_coord.Y--;
3844 }
3845 gotoxy(g_coord.X + 1, g_coord.Y + 1);
3846#ifdef MCH_WRITE_DUMP
3847 if (fdDump)
3848 fputs("\\b\n", fdDump);
3849#endif
3850 s++;
3851 }
3852 else if (s[0] == '\a')
3853 {
3854 /* \a, bell */
3855 MessageBeep(0xFFFFFFFF);
3856#ifdef MCH_WRITE_DUMP
3857 if (fdDump)
3858 fputs("\\a\n", fdDump);
3859#endif
3860 s++;
3861 }
3862 else if (s[0] == ESC && len >= 3-1 && s[1] == '|')
3863 {
3864#ifdef MCH_WRITE_DUMP
3865 char_u* old_s = s;
3866#endif
3867 char_u* p;
3868 int arg1 = 0, arg2 = 0;
3869
3870 switch (s[2])
3871 {
3872 /* one or two numeric arguments, separated by ';' */
3873
3874 case '0': case '1': case '2': case '3': case '4':
3875 case '5': case '6': case '7': case '8': case '9':
3876 p = s + 2;
3877 arg1 = getdigits(&p); /* no check for length! */
3878 if (p > s + len)
3879 break;
3880
3881 if (*p == ';')
3882 {
3883 ++p;
3884 arg2 = getdigits(&p); /* no check for length! */
3885 if (p > s + len)
3886 break;
3887
3888 if (*p == 'H')
3889 gotoxy(arg2, arg1);
3890 else if (*p == 'r')
3891 set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
3892 }
3893 else if (*p == 'A')
3894 {
3895 /* move cursor up arg1 lines in same column */
3896 gotoxy(g_coord.X + 1,
3897 max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
3898 }
3899 else if (*p == 'C')
3900 {
3901 /* move cursor right arg1 columns in same line */
3902 gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
3903 g_coord.Y + 1);
3904 }
3905 else if (*p == 'H')
3906 {
3907 gotoxy(1, arg1);
3908 }
3909 else if (*p == 'L')
3910 {
3911 insert_lines(arg1);
3912 }
3913 else if (*p == 'm')
3914 {
3915 if (arg1 == 0)
3916 normvideo();
3917 else
3918 textattr((WORD) arg1);
3919 }
3920 else if (*p == 'f')
3921 {
3922 textcolor((WORD) arg1);
3923 }
3924 else if (*p == 'b')
3925 {
3926 textbackground((WORD) arg1);
3927 }
3928 else if (*p == 'M')
3929 {
3930 delete_lines(arg1);
3931 }
3932
3933 len -= p - s;
3934 s = p + 1;
3935 break;
3936
3937
3938 /* Three-character escape sequences */
3939
3940 case 'A':
3941 /* move cursor up one line in same column */
3942 gotoxy(g_coord.X + 1,
3943 max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
3944 goto got3;
3945
3946 case 'B':
3947 visual_bell();
3948 goto got3;
3949
3950 case 'C':
3951 /* move cursor right one column in same line */
3952 gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
3953 g_coord.Y + 1);
3954 goto got3;
3955
3956 case 'E':
3957 termcap_mode_end();
3958 goto got3;
3959
3960 case 'F':
3961 standout();
3962 goto got3;
3963
3964 case 'f':
3965 standend();
3966 goto got3;
3967
3968 case 'H':
3969 gotoxy(1, 1);
3970 goto got3;
3971
3972 case 'j':
3973 clear_to_end_of_display();
3974 goto got3;
3975
3976 case 'J':
3977 clear_screen();
3978 goto got3;
3979
3980 case 'K':
3981 clear_to_end_of_line();
3982 goto got3;
3983
3984 case 'L':
3985 insert_lines(1);
3986 goto got3;
3987
3988 case 'M':
3989 delete_lines(1);
3990 goto got3;
3991
3992 case 'S':
3993 termcap_mode_start();
3994 goto got3;
3995
3996 case 'V':
3997 cursor_visible(TRUE);
3998 goto got3;
3999
4000 case 'v':
4001 cursor_visible(FALSE);
4002 goto got3;
4003
4004 got3:
4005 s += 3;
4006 len -= 2;
4007 }
4008
4009#ifdef MCH_WRITE_DUMP
4010 if (fdDump)
4011 {
4012 fputs("ESC | ", fdDump);
4013 fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
4014 fputc('\n', fdDump);
4015 }
4016#endif
4017 }
4018 else
4019 {
4020 /* Write a single character */
4021 DWORD nWritten;
4022
4023 nWritten = write_chars(s, 1);
4024#ifdef MCH_WRITE_DUMP
4025 if (fdDump)
4026 {
4027 fputc('>', fdDump);
4028 fwrite(s, sizeof(char_u), nWritten, fdDump);
4029 fputs("<\n", fdDump);
4030 }
4031#endif
4032
4033 len -= (nWritten - 1);
4034 s += nWritten;
4035 }
4036 }
4037
4038#ifdef MCH_WRITE_DUMP
4039 if (fdDump)
4040 fflush(fdDump);
4041#endif
4042}
4043
4044#endif /* FEAT_GUI_W32 */
4045
4046
4047/*
4048 * Delay for half a second.
4049 */
4050 void
4051mch_delay(
4052 long msec,
4053 int ignoreinput)
4054{
4055#ifdef FEAT_GUI_W32
4056 Sleep((int)msec); /* never wait for input */
4057#else
4058 if (ignoreinput)
4059 Sleep((int)msec);
4060 else
4061 WaitForChar(msec);
4062#endif
4063}
4064
4065
4066/*
4067 * this version of remove is not scared by a readonly (backup) file
4068 * Return 0 for success, -1 for failure.
4069 */
4070 int
4071mch_remove(char_u *name)
4072{
4073#ifdef FEAT_MBYTE
4074 WCHAR *wn = NULL;
4075 int n;
4076
4077 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4078 {
4079 wn = enc_to_ucs2(name, NULL);
4080 if (wn != NULL)
4081 {
4082 SetFileAttributesW(wn, FILE_ATTRIBUTE_NORMAL);
4083 n = DeleteFileW(wn) ? 0 : -1;
4084 vim_free(wn);
4085 if (n == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4086 return n;
4087 /* Retry with non-wide function (for Windows 98). */
4088 }
4089 }
4090#endif
4091 SetFileAttributes(name, FILE_ATTRIBUTE_NORMAL);
4092 return DeleteFile(name) ? 0 : -1;
4093}
4094
4095
4096/*
4097 * check for an "interrupt signal": CTRL-break or CTRL-C
4098 */
4099 void
4100mch_breakcheck()
4101{
4102#ifndef FEAT_GUI_W32 /* never used */
4103 if (g_fCtrlCPressed || g_fCBrkPressed)
4104 {
4105 g_fCtrlCPressed = g_fCBrkPressed = FALSE;
4106 got_int = TRUE;
4107 }
4108#endif
4109}
4110
4111
4112/*
4113 * How much memory is available?
4114 * Return sum of available physical and page file memory.
4115 */
4116 long_u
4117mch_avail_mem(
4118 int special)
4119{
4120 MEMORYSTATUS ms;
4121
4122 ms.dwLength = sizeof(MEMORYSTATUS);
4123 GlobalMemoryStatus(&ms);
4124 return (long_u) (ms.dwAvailPhys + ms.dwAvailPageFile);
4125}
4126
4127#ifdef FEAT_MBYTE
4128/*
4129 * Same code as below, but with wide functions and no comments.
4130 * Return 0 for success, non-zero for failure.
4131 */
4132 int
4133mch_wrename(WCHAR *wold, WCHAR *wnew)
4134{
4135 WCHAR *p;
4136 int i;
4137 WCHAR szTempFile[_MAX_PATH + 1];
4138 WCHAR szNewPath[_MAX_PATH + 1];
4139 HANDLE hf;
4140
4141 if (!mch_windows95())
4142 {
4143 p = wold;
4144 for (i = 0; wold[i] != NUL; ++i)
4145 if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':')
4146 && wold[i + 1] != 0)
4147 p = wold + i + 1;
4148 if ((int)(wold + i - p) < 8 || p[6] != '~')
4149 return (MoveFileW(wold, wnew) == 0);
4150 }
4151
4152 if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL)
4153 return -1;
4154 *p = NUL;
4155
4156 if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0)
4157 return -2;
4158
4159 if (!DeleteFileW(szTempFile))
4160 return -3;
4161
4162 if (!MoveFileW(wold, szTempFile))
4163 return -4;
4164
4165 if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW,
4166 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
4167 return -5;
4168 if (!CloseHandle(hf))
4169 return -6;
4170
4171 if (!MoveFileW(szTempFile, wnew))
4172 {
4173 (void)MoveFileW(szTempFile, wold);
4174 return -7;
4175 }
4176
4177 DeleteFileW(szTempFile);
4178
4179 if (!DeleteFileW(wold))
4180 return -8;
4181
4182 return 0;
4183}
4184#endif
4185
4186
4187/*
4188 * mch_rename() works around a bug in rename (aka MoveFile) in
4189 * Windows 95: rename("foo.bar", "foo.bar~") will generate a
4190 * file whose short file name is "FOO.BAR" (its long file name will
4191 * be correct: "foo.bar~"). Because a file can be accessed by
4192 * either its SFN or its LFN, "foo.bar" has effectively been
4193 * renamed to "foo.bar", which is not at all what was wanted. This
4194 * seems to happen only when renaming files with three-character
4195 * extensions by appending a suffix that does not include ".".
4196 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
4197 *
4198 * There is another problem, which isn't really a bug but isn't right either:
4199 * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be
4200 * "abcdef~1.txt" again. This has been reported on Windows NT 4.0 with
4201 * service pack 6. Doesn't seem to happen on Windows 98.
4202 *
4203 * Like rename(), returns 0 upon success, non-zero upon failure.
4204 * Should probably set errno appropriately when errors occur.
4205 */
4206 int
4207mch_rename(
4208 const char *pszOldFile,
4209 const char *pszNewFile)
4210{
4211 char szTempFile[_MAX_PATH+1];
4212 char szNewPath[_MAX_PATH+1];
4213 char *pszFilePart;
4214 HANDLE hf;
4215#ifdef FEAT_MBYTE
4216 WCHAR *wold = NULL;
4217 WCHAR *wnew = NULL;
4218 int retval = -1;
4219
4220 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4221 {
4222 wold = enc_to_ucs2((char_u *)pszOldFile, NULL);
4223 wnew = enc_to_ucs2((char_u *)pszNewFile, NULL);
4224 if (wold != NULL && wnew != NULL)
4225 retval = mch_wrename(wold, wnew);
4226 vim_free(wold);
4227 vim_free(wnew);
4228 if (retval == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4229 return retval;
4230 /* Retry with non-wide function (for Windows 98). */
4231 }
4232#endif
4233
4234 /*
4235 * No need to play tricks if not running Windows 95, unless the file name
4236 * contains a "~" as the seventh character.
4237 */
4238 if (!mch_windows95())
4239 {
4240 pszFilePart = (char *)gettail((char_u *)pszOldFile);
4241 if (STRLEN(pszFilePart) < 8 || pszFilePart[6] != '~')
4242 return rename(pszOldFile, pszNewFile);
4243 }
4244
4245 /* Get base path of new file name. Undocumented feature: If pszNewFile is
4246 * a directory, no error is returned and pszFilePart will be NULL. */
4247 if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0
4248 || pszFilePart == NULL)
4249 return -1;
4250 *pszFilePart = NUL;
4251
4252 /* Get (and create) a unique temporary file name in directory of new file */
4253 if (GetTempFileName(szNewPath, "VIM", 0, szTempFile) == 0)
4254 return -2;
4255
4256 /* blow the temp file away */
4257 if (!DeleteFile(szTempFile))
4258 return -3;
4259
4260 /* rename old file to the temp file */
4261 if (!MoveFile(pszOldFile, szTempFile))
4262 return -4;
4263
4264 /* now create an empty file called pszOldFile; this prevents the operating
4265 * system using pszOldFile as an alias (SFN) if we're renaming within the
4266 * same directory. For example, we're editing a file called
4267 * filename.asc.txt by its SFN, filena~1.txt. If we rename filena~1.txt
4268 * to filena~1.txt~ (i.e., we're making a backup while writing it), the
4269 * SFN for filena~1.txt~ will be filena~1.txt, by default, which will
4270 * cause all sorts of problems later in buf_write. So, we create an empty
4271 * file called filena~1.txt and the system will have to find some other
4272 * SFN for filena~1.txt~, such as filena~2.txt
4273 */
4274 if ((hf = CreateFile(pszOldFile, GENERIC_WRITE, 0, NULL, CREATE_NEW,
4275 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
4276 return -5;
4277 if (!CloseHandle(hf))
4278 return -6;
4279
4280 /* rename the temp file to the new file */
4281 if (!MoveFile(szTempFile, pszNewFile))
4282 {
4283 /* Renaming failed. Rename the file back to its old name, so that it
4284 * looks like nothing happened. */
4285 (void)MoveFile(szTempFile, pszOldFile);
4286
4287 return -7;
4288 }
4289
4290 /* Seems to be left around on Novell filesystems */
4291 DeleteFile(szTempFile);
4292
4293 /* finally, remove the empty old file */
4294 if (!DeleteFile(pszOldFile))
4295 return -8;
4296
4297 return 0; /* success */
4298}
4299
4300/*
4301 * Get the default shell for the current hardware platform
4302 */
4303 char *
4304default_shell()
4305{
4306 char* psz = NULL;
4307
4308 PlatformId();
4309
4310 if (g_PlatformId == VER_PLATFORM_WIN32_NT) /* Windows NT */
4311 psz = "cmd.exe";
4312 else if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS) /* Windows 95 */
4313 psz = "command.com";
4314
4315 return psz;
4316}
4317
4318/*
4319 * mch_access() extends access() to do more detailed check on network drives.
4320 * Returns 0 if file "n" has access rights according to "p", -1 otherwise.
4321 */
4322 int
4323mch_access(char *n, int p)
4324{
4325 HANDLE hFile;
4326 DWORD am;
4327 int retval = -1; /* default: fail */
4328#ifdef FEAT_MBYTE
4329 WCHAR *wn = NULL;
4330
4331 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
4332 wn = enc_to_ucs2(n, NULL);
4333#endif
4334
4335 if (mch_isdir(n))
4336 {
4337 char TempName[_MAX_PATH + 16] = "";
4338#ifdef FEAT_MBYTE
4339 WCHAR TempNameW[_MAX_PATH + 16] = L"";
4340#endif
4341
4342 if (p & R_OK)
4343 {
4344 /* Read check is performed by seeing if we can do a find file on
4345 * the directory for any file. */
4346#ifdef FEAT_MBYTE
4347 if (wn != NULL)
4348 {
4349 int i;
4350 WIN32_FIND_DATAW d;
4351
4352 for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i)
4353 TempNameW[i] = wn[i];
4354 if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/')
4355 TempNameW[i++] = '\\';
4356 TempNameW[i++] = '*';
4357 TempNameW[i++] = 0;
4358
4359 hFile = FindFirstFileW(TempNameW, &d);
4360 if (hFile == INVALID_HANDLE_VALUE)
4361 {
4362 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4363 goto getout;
4364
4365 /* Retry with non-wide function (for Windows 98). */
4366 vim_free(wn);
4367 wn = NULL;
4368 }
4369 else
4370 (void)FindClose(hFile);
4371 }
4372 if (wn == NULL)
4373#endif
4374 {
4375 char *pch;
4376 WIN32_FIND_DATA d;
4377
4378 STRNCPY(TempName, n, _MAX_PATH);
4379 pch = TempName + STRLEN(TempName) - 1;
4380 if (*pch != '\\' && *pch != '/')
4381 *++pch = '\\';
4382 *++pch = '*';
4383 *++pch = NUL;
4384
4385 hFile = FindFirstFile(TempName, &d);
4386 if (hFile == INVALID_HANDLE_VALUE)
4387 goto getout;
4388 (void)FindClose(hFile);
4389 }
4390 }
4391
4392 if (p & W_OK)
4393 {
4394 /* Trying to create a temporary file in the directory should catch
4395 * directories on read-only network shares. However, in
4396 * directories whose ACL allows writes but denies deletes will end
4397 * up keeping the temporary file :-(. */
4398#ifdef FEAT_MBYTE
4399 if (wn != NULL)
4400 {
4401 if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW))
4402 {
4403 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4404 goto getout;
4405
4406 /* Retry with non-wide function (for Windows 98). */
4407 vim_free(wn);
4408 wn = NULL;
4409 }
4410 else
4411 DeleteFileW(TempNameW);
4412 }
4413 if (wn == NULL)
4414#endif
4415 {
4416 if (!GetTempFileName(n, "VIM", 0, TempName))
4417 goto getout;
4418 mch_remove((char_u *)TempName);
4419 }
4420 }
4421 }
4422 else
4423 {
4424 /* Trying to open the file for the required access does ACL, read-only
4425 * network share, and file attribute checks. */
4426 am = ((p & W_OK) ? GENERIC_WRITE : 0)
4427 | ((p & R_OK) ? GENERIC_READ : 0);
4428#ifdef FEAT_MBYTE
4429 if (wn != NULL)
4430 {
4431 hFile = CreateFileW(wn, am, 0, NULL, OPEN_EXISTING, 0, NULL);
4432 if (hFile == INVALID_HANDLE_VALUE
4433 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
4434 {
4435 /* Retry with non-wide function (for Windows 98). */
4436 vim_free(wn);
4437 wn = NULL;
4438 }
4439 }
4440 if (wn == NULL)
4441#endif
4442 hFile = CreateFile(n, am, 0, NULL, OPEN_EXISTING, 0, NULL);
4443 if (hFile == INVALID_HANDLE_VALUE)
4444 goto getout;
4445 CloseHandle(hFile);
4446 }
4447
4448 retval = 0; /* success */
4449getout:
4450#ifdef FEAT_MBYTE
4451 vim_free(wn);
4452#endif
4453 return retval;
4454}
4455
4456#if defined(FEAT_MBYTE) || defined(PROTO)
4457/*
4458 * Version of open() that may use ucs2 file name.
4459 */
4460 int
4461mch_open(char *name, int flags, int mode)
4462{
4463 WCHAR *wn;
4464 int f;
4465
4466 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
4467# ifdef __BORLANDC__
4468 /* Wide functions of Borland C 5.5 do not work on Windows 98. */
4469 && g_PlatformId == VER_PLATFORM_WIN32_NT
4470# endif
4471 )
4472 {
4473 wn = enc_to_ucs2(name, NULL);
4474 if (wn != NULL)
4475 {
4476 f = _wopen(wn, flags, mode);
4477 vim_free(wn);
4478 if (f >= 0)
4479 return f;
4480 /* Retry with non-wide function (for Windows 98). Can't use
4481 * GetLastError() here and it's unclear what errno gets set to if
4482 * the _wopen() fails for missing wide functions. */
4483 }
4484 }
4485
4486 return open(name, flags, mode);
4487}
4488
4489/*
4490 * Version of fopen() that may use ucs2 file name.
4491 */
4492 FILE *
4493mch_fopen(char *name, char *mode)
4494{
4495 WCHAR *wn, *wm;
4496 FILE *f = NULL;
4497
4498 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
4499# ifdef __BORLANDC__
4500 /* Wide functions of Borland C 5.5 do not work on Windows 98. */
4501 && g_PlatformId == VER_PLATFORM_WIN32_NT
4502# endif
4503 )
4504 {
4505 wn = enc_to_ucs2(name, NULL);
4506 wm = enc_to_ucs2(mode, NULL);
4507 if (wn != NULL && wm != NULL)
4508 f = _wfopen(wn, wm);
4509 vim_free(wn);
4510 vim_free(wm);
4511 if (f != NULL)
4512 return f;
4513 /* Retry with non-wide function (for Windows 98). Can't use
4514 * GetLastError() here and it's unclear what errno gets set to if
4515 * the _wfopen() fails for missing wide functions. */
4516 }
4517
4518 return fopen(name, mode);
4519}
4520#endif
4521
4522#ifdef FEAT_MBYTE
4523/*
4524 * SUB STREAM (aka info stream) handling:
4525 *
4526 * NTFS can have sub streams for each file. Normal contents of file is
4527 * stored in the main stream, and extra contents (author information and
4528 * title and so on) can be stored in sub stream. After Windows 2000, user
4529 * can access and store those informations in sub streams via explorer's
4530 * property menuitem in right click menu. Those informations in sub streams
4531 * were lost when copying only the main stream. So we have to copy sub
4532 * streams.
4533 *
4534 * Incomplete explanation:
4535 * http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp
4536 * More useful info and an example:
4537 * http://www.sysinternals.com/ntw2k/source/misc.shtml#streams
4538 */
4539
4540/*
4541 * Copy info stream data "substream". Read from the file with BackupRead(sh)
4542 * and write to stream "substream" of file "to".
4543 * Errors are ignored.
4544 */
4545 static void
4546copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len)
4547{
4548 HANDLE hTo;
4549 WCHAR *to_name;
4550
4551 to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR));
4552 wcscpy(to_name, to);
4553 wcscat(to_name, substream);
4554
4555 hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
4556 FILE_ATTRIBUTE_NORMAL, NULL);
4557 if (hTo != INVALID_HANDLE_VALUE)
4558 {
4559 long done;
4560 DWORD todo;
4561 DWORD readcnt, written;
4562 char buf[4096];
4563
4564 /* Copy block of bytes at a time. Abort when something goes wrong. */
4565 for (done = 0; done < len; done += written)
4566 {
4567 /* (size_t) cast for Borland C 5.5 */
4568 todo = (size_t)(len - done) > sizeof(buf) ? sizeof(buf)
4569 : (size_t)(len - done);
4570 if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt,
4571 FALSE, FALSE, context)
4572 || readcnt != todo
4573 || !WriteFile(hTo, buf, todo, &written, NULL)
4574 || written != todo)
4575 break;
4576 }
4577 CloseHandle(hTo);
4578 }
4579
4580 free(to_name);
4581}
4582
4583/*
4584 * Copy info streams from file "from" to file "to".
4585 */
4586 static void
4587copy_infostreams(char_u *from, char_u *to)
4588{
4589 WCHAR *fromw;
4590 WCHAR *tow;
4591 HANDLE sh;
4592 WIN32_STREAM_ID sid;
4593 int headersize;
4594 WCHAR streamname[_MAX_PATH];
4595 DWORD readcount;
4596 void *context = NULL;
4597 DWORD lo, hi;
4598 int len;
4599
4600 /* Convert the file names to wide characters. */
4601 fromw = enc_to_ucs2(from, NULL);
4602 tow = enc_to_ucs2(to, NULL);
4603 if (fromw != NULL && tow != NULL)
4604 {
4605 /* Open the file for reading. */
4606 sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL,
4607 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
4608 if (sh != INVALID_HANDLE_VALUE)
4609 {
4610 /* Use BackupRead() to find the info streams. Repeat until we
4611 * have done them all.*/
4612 for (;;)
4613 {
4614 /* Get the header to find the length of the stream name. If
4615 * the "readcount" is zero we have done all info streams. */
4616 ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
4617 headersize = (char *)&sid.cStreamName - (char *)&sid.dwStreamId;
4618 if (!BackupRead(sh, (LPBYTE)&sid, headersize,
4619 &readcount, FALSE, FALSE, &context)
4620 || readcount == 0)
4621 break;
4622
4623 /* We only deal with streams that have a name. The normal
4624 * file data appears to be without a name, even though docs
4625 * suggest it is called "::$DATA". */
4626 if (sid.dwStreamNameSize > 0)
4627 {
4628 /* Read the stream name. */
4629 if (!BackupRead(sh, (LPBYTE)streamname,
4630 sid.dwStreamNameSize,
4631 &readcount, FALSE, FALSE, &context))
4632 break;
4633
4634 /* Copy an info stream with a name ":anything:$DATA".
4635 * Skip "::$DATA", it has no stream name (examples suggest
4636 * it might be used for the normal file contents).
4637 * Note that BackupRead() counts bytes, but the name is in
4638 * wide characters. */
4639 len = readcount / sizeof(WCHAR);
4640 streamname[len] = 0;
4641 if (len > 7 && wcsicmp(streamname + len - 6,
4642 L":$DATA") == 0)
4643 {
4644 streamname[len - 6] = 0;
4645 copy_substream(sh, &context, tow, streamname,
4646 (long)sid.Size.LowPart);
4647 }
4648 }
4649
4650 /* Advance to the next stream. We might try seeking too far,
4651 * but BackupSeek() doesn't skip over stream borders, thus
4652 * that's OK. */
4653 (void)BackupSeek(sh, sid.Size.LowPart, sid.Size.HighPart,
4654 &lo, &hi, &context);
4655 }
4656
4657 /* Clear the context. */
4658 (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context);
4659
4660 CloseHandle(sh);
4661 }
4662 }
4663 vim_free(fromw);
4664 vim_free(tow);
4665}
4666#endif
4667
4668/*
4669 * Copy file attributes from file "from" to file "to".
4670 * For Windows NT and later we copy info streams.
4671 * Always returns zero, errors are ignored.
4672 */
4673 int
4674mch_copy_file_attribute(char_u *from, char_u *to)
4675{
4676#ifdef FEAT_MBYTE
4677 /* File streams only work on Windows NT and later. */
4678 PlatformId();
4679 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
4680 copy_infostreams(from, to);
4681#endif
4682 return 0;
4683}
4684
4685#if defined(MYRESETSTKOFLW) || defined(PROTO)
4686/*
4687 * Recreate a destroyed stack guard page in win32.
4688 * Written by Benjamin Peterson.
4689 */
4690
4691/* These magic numbers are from the MS header files */
4692#define MIN_STACK_WIN9X 17
4693#define MIN_STACK_WINNT 2
4694
4695/*
4696 * This function does the same thing as _resetstkoflw(), which is only
4697 * available in DevStudio .net and later.
4698 * Returns 0 for failure, 1 for success.
4699 */
4700 int
4701myresetstkoflw(void)
4702{
4703 BYTE *pStackPtr;
4704 BYTE *pGuardPage;
4705 BYTE *pStackBase;
4706 BYTE *pLowestPossiblePage;
4707 MEMORY_BASIC_INFORMATION mbi;
4708 SYSTEM_INFO si;
4709 DWORD nPageSize;
4710 DWORD dummy;
4711
4712 /* This code will not work on win32s. */
4713 PlatformId();
4714 if (g_PlatformId == VER_PLATFORM_WIN32s)
4715 return 0;
4716
4717 /* We need to know the system page size. */
4718 GetSystemInfo(&si);
4719 nPageSize = si.dwPageSize;
4720
4721 /* ...and the current stack pointer */
4722 pStackPtr = (BYTE*)_alloca(1);
4723
4724 /* ...and the base of the stack. */
4725 if (VirtualQuery(pStackPtr, &mbi, sizeof mbi) == 0)
4726 return 0;
4727 pStackBase = (BYTE*)mbi.AllocationBase;
4728
4729 /* ...and the page thats min_stack_req pages away from stack base; this is
4730 * the lowest page we could use. */
4731 pLowestPossiblePage = pStackBase + ((g_PlatformId == VER_PLATFORM_WIN32_NT)
4732 ? MIN_STACK_WINNT : MIN_STACK_WIN9X) * nPageSize;
4733
4734 /* On Win95, we want the next page down from the end of the stack. */
4735 if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS)
4736 {
4737 /* Find the page that's only 1 page down from the page that the stack
4738 * ptr is in. */
4739 pGuardPage = (BYTE*)((DWORD)nPageSize * (((DWORD)pStackPtr
4740 / (DWORD)nPageSize) - 1));
4741 if (pGuardPage < pLowestPossiblePage)
4742 return 0;
4743
4744 /* Apply the noaccess attribute to the page -- there's no guard
4745 * attribute in win95-type OSes. */
4746 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_NOACCESS, &dummy))
4747 return 0;
4748 }
4749 else
4750 {
4751 /* On NT, however, we want the first committed page in the stack Start
4752 * at the stack base and move forward through memory until we find a
4753 * committed block. */
4754 BYTE *pBlock = pStackBase;
4755
4756 while (1)
4757 {
4758 if (VirtualQuery(pBlock, &mbi, sizeof mbi) == 0)
4759 return 0;
4760
4761 pBlock += mbi.RegionSize;
4762
4763 if (mbi.State & MEM_COMMIT)
4764 break;
4765 }
4766
4767 /* mbi now describes the first committed block in the stack. */
4768 if (mbi.Protect & PAGE_GUARD)
4769 return 1;
4770
4771 /* decide where the guard page should start */
4772 if ((long_u)(mbi.BaseAddress) < (long_u)pLowestPossiblePage)
4773 pGuardPage = pLowestPossiblePage;
4774 else
4775 pGuardPage = (BYTE*)mbi.BaseAddress;
4776
4777 /* allocate the guard page */
4778 if (!VirtualAlloc(pGuardPage, nPageSize, MEM_COMMIT, PAGE_READWRITE))
4779 return 0;
4780
4781 /* apply the guard attribute to the page */
4782 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_READWRITE | PAGE_GUARD,
4783 &dummy))
4784 return 0;
4785 }
4786
4787 return 1;
4788}
4789
4790#endif