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