1 /* vi:set ts=8 sts=4 sw=4 noet: 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_MSWIN". 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 "vim.h" 24 25 #ifdef FEAT_MZSCHEME 26 # include "if_mzsch.h" 27 #endif 28 29 #include <sys/types.h> 30 #include <signal.h> 31 #include <limits.h> 32 33 // cproto fails on missing include files 34 #ifndef PROTO 35 # include <process.h> 36 # include <winternl.h> 37 #endif 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 #ifndef PROTO 49 # if defined(FEAT_TITLE) && !defined(FEAT_GUI_MSWIN) 50 # include <shellapi.h> 51 # endif 52 #endif 53 54 #ifdef FEAT_JOB_CHANNEL 55 # include <tlhelp32.h> 56 #endif 57 58 #ifdef __MINGW32__ 59 # ifndef FROM_LEFT_1ST_BUTTON_PRESSED 60 # define FROM_LEFT_1ST_BUTTON_PRESSED 0x0001 61 # endif 62 # ifndef RIGHTMOST_BUTTON_PRESSED 63 # define RIGHTMOST_BUTTON_PRESSED 0x0002 64 # endif 65 # ifndef FROM_LEFT_2ND_BUTTON_PRESSED 66 # define FROM_LEFT_2ND_BUTTON_PRESSED 0x0004 67 # endif 68 # ifndef FROM_LEFT_3RD_BUTTON_PRESSED 69 # define FROM_LEFT_3RD_BUTTON_PRESSED 0x0008 70 # endif 71 # ifndef FROM_LEFT_4TH_BUTTON_PRESSED 72 # define FROM_LEFT_4TH_BUTTON_PRESSED 0x0010 73 # endif 74 75 /* 76 * EventFlags 77 */ 78 # ifndef MOUSE_MOVED 79 # define MOUSE_MOVED 0x0001 80 # endif 81 # ifndef DOUBLE_CLICK 82 # define DOUBLE_CLICK 0x0002 83 # endif 84 #endif 85 86 // Record all output and all keyboard & mouse input 87 // #define MCH_WRITE_DUMP 88 89 #ifdef MCH_WRITE_DUMP 90 FILE* fdDump = NULL; 91 #endif 92 93 /* 94 * When generating prototypes for Win32 on Unix, these lines make the syntax 95 * errors disappear. They do not need to be correct. 96 */ 97 #ifdef PROTO 98 # define WINAPI 99 typedef char * LPCSTR; 100 typedef char * LPWSTR; 101 typedef int ACCESS_MASK; 102 typedef int BOOL; 103 typedef int COLORREF; 104 typedef int CONSOLE_CURSOR_INFO; 105 typedef int COORD; 106 typedef int DWORD; 107 typedef int HANDLE; 108 typedef int LPHANDLE; 109 typedef int HDC; 110 typedef int HFONT; 111 typedef int HICON; 112 typedef int HINSTANCE; 113 typedef int HWND; 114 typedef int INPUT_RECORD; 115 typedef int INT; 116 typedef int KEY_EVENT_RECORD; 117 typedef int LOGFONT; 118 typedef int LPBOOL; 119 typedef int LPCTSTR; 120 typedef int LPDWORD; 121 typedef int LPSTR; 122 typedef int LPTSTR; 123 typedef int LPVOID; 124 typedef int MOUSE_EVENT_RECORD; 125 typedef int PACL; 126 typedef int PDWORD; 127 typedef int PHANDLE; 128 typedef int PRINTDLG; 129 typedef int PSECURITY_DESCRIPTOR; 130 typedef int PSID; 131 typedef int SECURITY_INFORMATION; 132 typedef int SHORT; 133 typedef int SMALL_RECT; 134 typedef int TEXTMETRIC; 135 typedef int TOKEN_INFORMATION_CLASS; 136 typedef int TRUSTEE; 137 typedef int WORD; 138 typedef int WCHAR; 139 typedef void VOID; 140 typedef int BY_HANDLE_FILE_INFORMATION; 141 typedef int SE_OBJECT_TYPE; 142 typedef int PSNSECINFO; 143 typedef int PSNSECINFOW; 144 typedef int STARTUPINFO; 145 typedef int PROCESS_INFORMATION; 146 typedef int LPSECURITY_ATTRIBUTES; 147 # define __stdcall // empty 148 #endif 149 150 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 151 // Win32 Console handles for input and output 152 static HANDLE g_hConIn = INVALID_HANDLE_VALUE; 153 static HANDLE g_hConOut = INVALID_HANDLE_VALUE; 154 155 // Win32 Screen buffer,coordinate,console I/O information 156 static SMALL_RECT g_srScrollRegion; 157 static COORD g_coord; // 0-based, but external coords are 1-based 158 159 // The attribute of the screen when the editor was started 160 static WORD g_attrDefault = 7; // lightgray text on black background 161 static WORD g_attrCurrent; 162 163 static int g_fCBrkPressed = FALSE; // set by ctrl-break interrupt 164 static int g_fCtrlCPressed = FALSE; // set when ctrl-C or ctrl-break detected 165 static int g_fForceExit = FALSE; // set when forcefully exiting 166 167 static void scroll(unsigned cLines); 168 static void set_scroll_region(unsigned left, unsigned top, 169 unsigned right, unsigned bottom); 170 static void set_scroll_region_tb(unsigned top, unsigned bottom); 171 static void set_scroll_region_lr(unsigned left, unsigned right); 172 static void insert_lines(unsigned cLines); 173 static void delete_lines(unsigned cLines); 174 static void gotoxy(unsigned x, unsigned y); 175 static void standout(void); 176 static int s_cursor_visible = TRUE; 177 static int did_create_conin = FALSE; 178 #endif 179 #ifdef FEAT_GUI_MSWIN 180 static int s_dont_use_vimrun = TRUE; 181 static int need_vimrun_warning = FALSE; 182 static char *vimrun_path = "vimrun "; 183 #endif 184 185 static int win32_getattrs(char_u *name); 186 static int win32_setattrs(char_u *name, int attrs); 187 static int win32_set_archive(char_u *name); 188 189 static int conpty_working = 0; 190 static int conpty_type = 0; 191 static int conpty_stable = 0; 192 static int conpty_fix_type = 0; 193 static void vtp_flag_init(); 194 195 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 196 static int vtp_working = 0; 197 static void vtp_init(); 198 static void vtp_exit(); 199 static void vtp_sgr_bulk(int arg); 200 static void vtp_sgr_bulks(int argc, int *argv); 201 202 static int wt_working = 0; 203 static void wt_init(); 204 205 static guicolor_T save_console_bg_rgb; 206 static guicolor_T save_console_fg_rgb; 207 static guicolor_T store_console_bg_rgb; 208 static guicolor_T store_console_fg_rgb; 209 210 static int g_color_index_bg = 0; 211 static int g_color_index_fg = 7; 212 213 # ifdef FEAT_TERMGUICOLORS 214 static int default_console_color_bg = 0x000000; // black 215 static int default_console_color_fg = 0xc0c0c0; // white 216 # endif 217 218 # ifdef FEAT_TERMGUICOLORS 219 # define USE_VTP (vtp_working && is_term_win32() && (p_tgc || (!p_tgc && t_colors >= 256))) 220 # define USE_WT (wt_working) 221 # else 222 # define USE_VTP 0 223 # define USE_WT 0 224 # endif 225 226 static void set_console_color_rgb(void); 227 static void reset_console_color_rgb(void); 228 static void restore_console_color_rgb(void); 229 #endif 230 231 // This flag is newly created from Windows 10 232 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING 233 # define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 234 #endif 235 236 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 237 static int suppress_winsize = 1; // don't fiddle with console 238 #endif 239 240 static char_u *exe_path = NULL; 241 242 static BOOL win8_or_later = FALSE; 243 244 # if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__) 245 # define UChar UnicodeChar 246 # else 247 # define UChar uChar.UnicodeChar 248 # endif 249 250 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 251 // Dynamic loading for portability 252 typedef struct _DYN_CONSOLE_SCREEN_BUFFER_INFOEX 253 { 254 ULONG cbSize; 255 COORD dwSize; 256 COORD dwCursorPosition; 257 WORD wAttributes; 258 SMALL_RECT srWindow; 259 COORD dwMaximumWindowSize; 260 WORD wPopupAttributes; 261 BOOL bFullscreenSupported; 262 COLORREF ColorTable[16]; 263 } DYN_CONSOLE_SCREEN_BUFFER_INFOEX, *PDYN_CONSOLE_SCREEN_BUFFER_INFOEX; 264 typedef BOOL (WINAPI *PfnGetConsoleScreenBufferInfoEx)(HANDLE, PDYN_CONSOLE_SCREEN_BUFFER_INFOEX); 265 static PfnGetConsoleScreenBufferInfoEx pGetConsoleScreenBufferInfoEx; 266 typedef BOOL (WINAPI *PfnSetConsoleScreenBufferInfoEx)(HANDLE, PDYN_CONSOLE_SCREEN_BUFFER_INFOEX); 267 static PfnSetConsoleScreenBufferInfoEx pSetConsoleScreenBufferInfoEx; 268 static BOOL has_csbiex = FALSE; 269 #endif 270 271 /* 272 * Get version number including build number 273 */ 274 typedef BOOL (WINAPI *PfnRtlGetVersion)(LPOSVERSIONINFOW); 275 #define MAKE_VER(major, minor, build) \ 276 (((major) << 24) | ((minor) << 16) | (build)) 277 278 static DWORD 279 get_build_number(void) 280 { 281 OSVERSIONINFOW osver = {sizeof(OSVERSIONINFOW)}; 282 HMODULE hNtdll; 283 PfnRtlGetVersion pRtlGetVersion; 284 DWORD ver = MAKE_VER(0, 0, 0); 285 286 hNtdll = GetModuleHandle("ntdll.dll"); 287 if (hNtdll != NULL) 288 { 289 pRtlGetVersion = 290 (PfnRtlGetVersion)GetProcAddress(hNtdll, "RtlGetVersion"); 291 pRtlGetVersion(&osver); 292 ver = MAKE_VER(min(osver.dwMajorVersion, 255), 293 min(osver.dwMinorVersion, 255), 294 min(osver.dwBuildNumber, 32767)); 295 } 296 return ver; 297 } 298 299 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 300 static BOOL 301 is_ambiwidth_event( 302 INPUT_RECORD *ir) 303 { 304 return ir->EventType == KEY_EVENT 305 && ir->Event.KeyEvent.bKeyDown 306 && ir->Event.KeyEvent.wRepeatCount == 1 307 && ir->Event.KeyEvent.wVirtualKeyCode == 0x12 308 && ir->Event.KeyEvent.wVirtualScanCode == 0x38 309 && ir->Event.KeyEvent.UChar == 0 310 && ir->Event.KeyEvent.dwControlKeyState == 2; 311 } 312 313 static void 314 make_ambiwidth_event( 315 INPUT_RECORD *down, 316 INPUT_RECORD *up) 317 { 318 down->Event.KeyEvent.wVirtualKeyCode = 0; 319 down->Event.KeyEvent.wVirtualScanCode = 0; 320 down->Event.KeyEvent.UChar = up->Event.KeyEvent.UChar; 321 down->Event.KeyEvent.dwControlKeyState = 0; 322 } 323 324 /* 325 * Version of ReadConsoleInput() that works with IME. 326 * Works around problems on Windows 8. 327 */ 328 static BOOL 329 read_console_input( 330 HANDLE hInput, 331 INPUT_RECORD *lpBuffer, 332 DWORD nLength, 333 LPDWORD lpEvents) 334 { 335 enum 336 { 337 IRSIZE = 10 338 }; 339 static INPUT_RECORD s_irCache[IRSIZE]; 340 static DWORD s_dwIndex = 0; 341 static DWORD s_dwMax = 0; 342 DWORD dwEvents; 343 int head; 344 int tail; 345 int i; 346 static INPUT_RECORD s_irPseudo; 347 348 if (nLength == -2) 349 return (s_dwMax > 0) ? TRUE : FALSE; 350 351 if (!win8_or_later) 352 { 353 if (nLength == -1) 354 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents); 355 return ReadConsoleInputW(hInput, lpBuffer, 1, &dwEvents); 356 } 357 358 if (s_dwMax == 0) 359 { 360 if (!USE_WT && nLength == -1) 361 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents); 362 GetNumberOfConsoleInputEvents(hInput, &dwEvents); 363 if (dwEvents == 0 && nLength == -1) 364 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents); 365 ReadConsoleInputW(hInput, s_irCache, IRSIZE, &dwEvents); 366 s_dwIndex = 0; 367 s_dwMax = dwEvents; 368 if (dwEvents == 0) 369 { 370 *lpEvents = 0; 371 return TRUE; 372 } 373 374 for (i = s_dwIndex; i < (int)s_dwMax - 1; ++i) 375 if (is_ambiwidth_event(&s_irCache[i])) 376 make_ambiwidth_event(&s_irCache[i], &s_irCache[i + 1]); 377 378 if (s_dwMax > 1) 379 { 380 head = 0; 381 tail = s_dwMax - 1; 382 while (head != tail) 383 { 384 if (s_irCache[head].EventType == WINDOW_BUFFER_SIZE_EVENT 385 && s_irCache[head + 1].EventType 386 == WINDOW_BUFFER_SIZE_EVENT) 387 { 388 // Remove duplicate event to avoid flicker. 389 for (i = head; i < tail; ++i) 390 s_irCache[i] = s_irCache[i + 1]; 391 --tail; 392 continue; 393 } 394 head++; 395 } 396 s_dwMax = tail + 1; 397 } 398 } 399 400 if (s_irCache[s_dwIndex].EventType == KEY_EVENT) 401 { 402 if (s_irCache[s_dwIndex].Event.KeyEvent.wRepeatCount > 1) 403 { 404 s_irPseudo = s_irCache[s_dwIndex]; 405 s_irPseudo.Event.KeyEvent.wRepeatCount = 1; 406 s_irCache[s_dwIndex].Event.KeyEvent.wRepeatCount--; 407 *lpBuffer = s_irPseudo; 408 *lpEvents = 1; 409 return TRUE; 410 } 411 } 412 413 *lpBuffer = s_irCache[s_dwIndex]; 414 if (!(nLength == -1 || nLength == -2) && ++s_dwIndex >= s_dwMax) 415 s_dwMax = 0; 416 *lpEvents = 1; 417 return TRUE; 418 } 419 420 /* 421 * Version of PeekConsoleInput() that works with IME. 422 */ 423 static BOOL 424 peek_console_input( 425 HANDLE hInput, 426 INPUT_RECORD *lpBuffer, 427 DWORD nLength UNUSED, 428 LPDWORD lpEvents) 429 { 430 return read_console_input(hInput, lpBuffer, -1, lpEvents); 431 } 432 433 # ifdef FEAT_CLIENTSERVER 434 static DWORD 435 msg_wait_for_multiple_objects( 436 DWORD nCount, 437 LPHANDLE pHandles, 438 BOOL fWaitAll, 439 DWORD dwMilliseconds, 440 DWORD dwWakeMask) 441 { 442 if (read_console_input(NULL, NULL, -2, NULL)) 443 return WAIT_OBJECT_0; 444 return MsgWaitForMultipleObjects(nCount, pHandles, fWaitAll, 445 dwMilliseconds, dwWakeMask); 446 } 447 # endif 448 449 # ifndef FEAT_CLIENTSERVER 450 static DWORD 451 wait_for_single_object( 452 HANDLE hHandle, 453 DWORD dwMilliseconds) 454 { 455 if (read_console_input(NULL, NULL, -2, NULL)) 456 return WAIT_OBJECT_0; 457 return WaitForSingleObject(hHandle, dwMilliseconds); 458 } 459 # endif 460 #endif 461 462 static void 463 get_exe_name(void) 464 { 465 // Maximum length of $PATH is more than MAXPATHL. 8191 is often mentioned 466 // as the maximum length that works (plus a NUL byte). 467 #define MAX_ENV_PATH_LEN 8192 468 char temp[MAX_ENV_PATH_LEN]; 469 char_u *p; 470 471 if (exe_name == NULL) 472 { 473 // store the name of the executable, may be used for $VIM 474 GetModuleFileName(NULL, temp, MAX_ENV_PATH_LEN - 1); 475 if (*temp != NUL) 476 exe_name = FullName_save((char_u *)temp, FALSE); 477 } 478 479 if (exe_path == NULL && exe_name != NULL) 480 { 481 exe_path = vim_strnsave(exe_name, gettail_sep(exe_name) - exe_name); 482 if (exe_path != NULL) 483 { 484 // Append our starting directory to $PATH, so that when doing 485 // "!xxd" it's found in our starting directory. Needed because 486 // SearchPath() also looks there. 487 p = mch_getenv("PATH"); 488 if (p == NULL 489 || STRLEN(p) + STRLEN(exe_path) + 2 < MAX_ENV_PATH_LEN) 490 { 491 if (p == NULL || *p == NUL) 492 temp[0] = NUL; 493 else 494 { 495 STRCPY(temp, p); 496 STRCAT(temp, ";"); 497 } 498 STRCAT(temp, exe_path); 499 vim_setenv((char_u *)"PATH", (char_u *)temp); 500 } 501 } 502 } 503 } 504 505 /* 506 * Unescape characters in "p" that appear in "escaped". 507 */ 508 static void 509 unescape_shellxquote(char_u *p, char_u *escaped) 510 { 511 int l = (int)STRLEN(p); 512 int n; 513 514 while (*p != NUL) 515 { 516 if (*p == '^' && vim_strchr(escaped, p[1]) != NULL) 517 mch_memmove(p, p + 1, l--); 518 n = (*mb_ptr2len)(p); 519 p += n; 520 l -= n; 521 } 522 } 523 524 /* 525 * Load library "name". 526 */ 527 HINSTANCE 528 vimLoadLib(char *name) 529 { 530 HINSTANCE dll = NULL; 531 532 // No need to load any library when registering OLE. 533 if (found_register_arg) 534 return dll; 535 536 // NOTE: Do not use mch_dirname() and mch_chdir() here, they may call 537 // vimLoadLib() recursively, which causes a stack overflow. 538 if (exe_path == NULL) 539 get_exe_name(); 540 if (exe_path != NULL) 541 { 542 WCHAR old_dirw[MAXPATHL]; 543 544 if (GetCurrentDirectoryW(MAXPATHL, old_dirw) != 0) 545 { 546 // Change directory to where the executable is, both to make 547 // sure we find a .dll there and to avoid looking for a .dll 548 // in the current directory. 549 SetCurrentDirectory((LPCSTR)exe_path); 550 dll = LoadLibrary(name); 551 SetCurrentDirectoryW(old_dirw); 552 return dll; 553 } 554 } 555 return dll; 556 } 557 558 #if defined(VIMDLL) || defined(PROTO) 559 /* 560 * Check if the current executable file is for the GUI subsystem. 561 */ 562 int 563 mch_is_gui_executable(void) 564 { 565 PBYTE pImage = (PBYTE)GetModuleHandle(NULL); 566 PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)pImage; 567 PIMAGE_NT_HEADERS pPE; 568 569 if (pDOS->e_magic != IMAGE_DOS_SIGNATURE) 570 return FALSE; 571 pPE = (PIMAGE_NT_HEADERS)(pImage + pDOS->e_lfanew); 572 if (pPE->Signature != IMAGE_NT_SIGNATURE) 573 return FALSE; 574 if (pPE->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) 575 return TRUE; 576 return FALSE; 577 } 578 #endif 579 580 #if defined(DYNAMIC_ICONV) || defined(DYNAMIC_GETTEXT) || defined(PROTO) 581 /* 582 * Get related information about 'funcname' which is imported by 'hInst'. 583 * If 'info' is 0, return the function address. 584 * If 'info' is 1, return the module name which the function is imported from. 585 */ 586 static void * 587 get_imported_func_info(HINSTANCE hInst, const char *funcname, int info) 588 { 589 PBYTE pImage = (PBYTE)hInst; 590 PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)hInst; 591 PIMAGE_NT_HEADERS pPE; 592 PIMAGE_IMPORT_DESCRIPTOR pImpDesc; 593 PIMAGE_THUNK_DATA pIAT; // Import Address Table 594 PIMAGE_THUNK_DATA pINT; // Import Name Table 595 PIMAGE_IMPORT_BY_NAME pImpName; 596 597 if (pDOS->e_magic != IMAGE_DOS_SIGNATURE) 598 return NULL; 599 pPE = (PIMAGE_NT_HEADERS)(pImage + pDOS->e_lfanew); 600 if (pPE->Signature != IMAGE_NT_SIGNATURE) 601 return NULL; 602 pImpDesc = (PIMAGE_IMPORT_DESCRIPTOR)(pImage 603 + pPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] 604 .VirtualAddress); 605 for (; pImpDesc->FirstThunk; ++pImpDesc) 606 { 607 if (!pImpDesc->OriginalFirstThunk) 608 continue; 609 pIAT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->FirstThunk); 610 pINT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->OriginalFirstThunk); 611 for (; pIAT->u1.Function; ++pIAT, ++pINT) 612 { 613 if (IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal)) 614 continue; 615 pImpName = (PIMAGE_IMPORT_BY_NAME)(pImage 616 + (UINT_PTR)(pINT->u1.AddressOfData)); 617 if (strcmp((char *)pImpName->Name, funcname) == 0) 618 { 619 switch (info) 620 { 621 case 0: 622 return (void *)pIAT->u1.Function; 623 case 1: 624 return (void *)(pImage + pImpDesc->Name); 625 default: 626 return NULL; 627 } 628 } 629 } 630 } 631 return NULL; 632 } 633 634 /* 635 * Get the module handle which 'funcname' in 'hInst' is imported from. 636 */ 637 HINSTANCE 638 find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname) 639 { 640 char *modulename; 641 642 modulename = (char *)get_imported_func_info(hInst, funcname, 1); 643 if (modulename != NULL) 644 return GetModuleHandleA(modulename); 645 return NULL; 646 } 647 648 /* 649 * Get the address of 'funcname' which is imported by 'hInst' DLL. 650 */ 651 void * 652 get_dll_import_func(HINSTANCE hInst, const char *funcname) 653 { 654 return get_imported_func_info(hInst, funcname, 0); 655 } 656 #endif 657 658 #if defined(DYNAMIC_GETTEXT) || defined(PROTO) 659 # ifndef GETTEXT_DLL 660 # define GETTEXT_DLL "libintl.dll" 661 # define GETTEXT_DLL_ALT1 "libintl-8.dll" 662 # define GETTEXT_DLL_ALT2 "intl.dll" 663 # endif 664 // Dummy functions 665 static char *null_libintl_gettext(const char *); 666 static char *null_libintl_ngettext(const char *, const char *, unsigned long n); 667 static char *null_libintl_textdomain(const char *); 668 static char *null_libintl_bindtextdomain(const char *, const char *); 669 static char *null_libintl_bind_textdomain_codeset(const char *, const char *); 670 static int null_libintl_wputenv(const wchar_t *); 671 672 static HINSTANCE hLibintlDLL = NULL; 673 char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext; 674 char *(*dyn_libintl_ngettext)(const char *, const char *, unsigned long n) 675 = null_libintl_ngettext; 676 char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain; 677 char *(*dyn_libintl_bindtextdomain)(const char *, const char *) 678 = null_libintl_bindtextdomain; 679 char *(*dyn_libintl_bind_textdomain_codeset)(const char *, const char *) 680 = null_libintl_bind_textdomain_codeset; 681 int (*dyn_libintl_wputenv)(const wchar_t *) = null_libintl_wputenv; 682 683 int 684 dyn_libintl_init(void) 685 { 686 int i; 687 static struct 688 { 689 char *name; 690 FARPROC *ptr; 691 } libintl_entry[] = 692 { 693 {"gettext", (FARPROC*)&dyn_libintl_gettext}, 694 {"ngettext", (FARPROC*)&dyn_libintl_ngettext}, 695 {"textdomain", (FARPROC*)&dyn_libintl_textdomain}, 696 {"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain}, 697 {NULL, NULL} 698 }; 699 HINSTANCE hmsvcrt; 700 701 // No need to initialize twice. 702 if (hLibintlDLL != NULL) 703 return 1; 704 // Load gettext library (libintl.dll and other names). 705 hLibintlDLL = vimLoadLib(GETTEXT_DLL); 706 # ifdef GETTEXT_DLL_ALT1 707 if (!hLibintlDLL) 708 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT1); 709 # endif 710 # ifdef GETTEXT_DLL_ALT2 711 if (!hLibintlDLL) 712 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT2); 713 # endif 714 if (!hLibintlDLL) 715 { 716 if (p_verbose > 0) 717 { 718 verbose_enter(); 719 semsg(_(e_loadlib), GETTEXT_DLL); 720 verbose_leave(); 721 } 722 return 0; 723 } 724 for (i = 0; libintl_entry[i].name != NULL 725 && libintl_entry[i].ptr != NULL; ++i) 726 { 727 if ((*libintl_entry[i].ptr = (FARPROC)GetProcAddress(hLibintlDLL, 728 libintl_entry[i].name)) == NULL) 729 { 730 dyn_libintl_end(); 731 if (p_verbose > 0) 732 { 733 verbose_enter(); 734 semsg(_(e_loadfunc), libintl_entry[i].name); 735 verbose_leave(); 736 } 737 return 0; 738 } 739 } 740 741 // The bind_textdomain_codeset() function is optional. 742 dyn_libintl_bind_textdomain_codeset = (void *)GetProcAddress(hLibintlDLL, 743 "bind_textdomain_codeset"); 744 if (dyn_libintl_bind_textdomain_codeset == NULL) 745 dyn_libintl_bind_textdomain_codeset = 746 null_libintl_bind_textdomain_codeset; 747 748 // _wputenv() function for the libintl.dll is optional. 749 hmsvcrt = find_imported_module_by_funcname(hLibintlDLL, "getenv"); 750 if (hmsvcrt != NULL) 751 dyn_libintl_wputenv = (void *)GetProcAddress(hmsvcrt, "_wputenv"); 752 if (dyn_libintl_wputenv == NULL || dyn_libintl_wputenv == _wputenv) 753 dyn_libintl_wputenv = null_libintl_wputenv; 754 755 return 1; 756 } 757 758 void 759 dyn_libintl_end(void) 760 { 761 if (hLibintlDLL) 762 FreeLibrary(hLibintlDLL); 763 hLibintlDLL = NULL; 764 dyn_libintl_gettext = null_libintl_gettext; 765 dyn_libintl_ngettext = null_libintl_ngettext; 766 dyn_libintl_textdomain = null_libintl_textdomain; 767 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain; 768 dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset; 769 dyn_libintl_wputenv = null_libintl_wputenv; 770 } 771 772 static char * 773 null_libintl_gettext(const char *msgid) 774 { 775 return (char*)msgid; 776 } 777 778 static char * 779 null_libintl_ngettext( 780 const char *msgid, 781 const char *msgid_plural, 782 unsigned long n) 783 { 784 return (char *)(n == 1 ? msgid : msgid_plural); 785 } 786 787 static char * 788 null_libintl_bindtextdomain( 789 const char *domainname UNUSED, 790 const char *dirname UNUSED) 791 { 792 return NULL; 793 } 794 795 static char * 796 null_libintl_bind_textdomain_codeset( 797 const char *domainname UNUSED, 798 const char *codeset UNUSED) 799 { 800 return NULL; 801 } 802 803 static char * 804 null_libintl_textdomain(const char *domainname UNUSED) 805 { 806 return NULL; 807 } 808 809 static int 810 null_libintl_wputenv(const wchar_t *envstring UNUSED) 811 { 812 return 0; 813 } 814 815 #endif // DYNAMIC_GETTEXT 816 817 // This symbol is not defined in older versions of the SDK or Visual C++ 818 819 #ifndef VER_PLATFORM_WIN32_WINDOWS 820 # define VER_PLATFORM_WIN32_WINDOWS 1 821 #endif 822 823 #ifdef HAVE_ACL 824 # ifndef PROTO 825 # include <aclapi.h> 826 # endif 827 # ifndef PROTECTED_DACL_SECURITY_INFORMATION 828 # define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000L 829 # endif 830 #endif 831 832 #ifdef HAVE_ACL 833 /* 834 * Enables or disables the specified privilege. 835 */ 836 static BOOL 837 win32_enable_privilege(LPTSTR lpszPrivilege, BOOL bEnable) 838 { 839 BOOL bResult; 840 LUID luid; 841 HANDLE hToken; 842 TOKEN_PRIVILEGES tokenPrivileges; 843 844 if (!OpenProcessToken(GetCurrentProcess(), 845 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) 846 return FALSE; 847 848 if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid)) 849 { 850 CloseHandle(hToken); 851 return FALSE; 852 } 853 854 tokenPrivileges.PrivilegeCount = 1; 855 tokenPrivileges.Privileges[0].Luid = luid; 856 tokenPrivileges.Privileges[0].Attributes = bEnable ? 857 SE_PRIVILEGE_ENABLED : 0; 858 859 bResult = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 860 sizeof(TOKEN_PRIVILEGES), NULL, NULL); 861 862 CloseHandle(hToken); 863 864 return bResult && GetLastError() == ERROR_SUCCESS; 865 } 866 #endif 867 868 #ifdef _MSC_VER 869 // Suppress the deprecation warning for using GetVersionEx(). 870 // It is needed for implementing "windowsversion()". 871 # pragma warning(push) 872 # pragma warning(disable: 4996) 873 #endif 874 /* 875 * Set "win8_or_later" and fill in "windowsVersion" if possible. 876 */ 877 void 878 PlatformId(void) 879 { 880 static int done = FALSE; 881 882 if (!done) 883 { 884 OSVERSIONINFO ovi; 885 886 ovi.dwOSVersionInfoSize = sizeof(ovi); 887 GetVersionEx(&ovi); 888 889 #ifdef FEAT_EVAL 890 vim_snprintf(windowsVersion, sizeof(windowsVersion), "%d.%d", 891 (int)ovi.dwMajorVersion, (int)ovi.dwMinorVersion); 892 #endif 893 if ((ovi.dwMajorVersion == 6 && ovi.dwMinorVersion >= 2) 894 || ovi.dwMajorVersion > 6) 895 win8_or_later = TRUE; 896 897 #ifdef HAVE_ACL 898 // Enable privilege for getting or setting SACLs. 899 win32_enable_privilege(SE_SECURITY_NAME, TRUE); 900 #endif 901 done = TRUE; 902 } 903 } 904 #ifdef _MSC_VER 905 # pragma warning(pop) 906 #endif 907 908 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 909 910 # define SHIFT (SHIFT_PRESSED) 911 # define CTRL (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED) 912 # define ALT (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) 913 # define ALT_GR (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED) 914 915 916 // When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode. 917 // We map function keys to their ANSI terminal equivalents, as produced 918 // by ANSI.SYS, for compatibility with the MS-DOS version of Vim. Any 919 // ANSI key with a value >= '\300' is nonstandard, but provided anyway 920 // so that the user can have access to all SHIFT-, CTRL-, and ALT- 921 // combinations of function/arrow/etc keys. 922 923 static const struct 924 { 925 WORD wVirtKey; 926 BOOL fAnsiKey; 927 int chAlone; 928 int chShift; 929 int chCtrl; 930 int chAlt; 931 } VirtKeyMap[] = 932 { 933 // Key ANSI alone shift ctrl alt 934 { VK_ESCAPE,FALSE, ESC, ESC, ESC, ESC, }, 935 936 { VK_F1, TRUE, ';', 'T', '^', 'h', }, 937 { VK_F2, TRUE, '<', 'U', '_', 'i', }, 938 { VK_F3, TRUE, '=', 'V', '`', 'j', }, 939 { VK_F4, TRUE, '>', 'W', 'a', 'k', }, 940 { VK_F5, TRUE, '?', 'X', 'b', 'l', }, 941 { VK_F6, TRUE, '@', 'Y', 'c', 'm', }, 942 { VK_F7, TRUE, 'A', 'Z', 'd', 'n', }, 943 { VK_F8, TRUE, 'B', '[', 'e', 'o', }, 944 { VK_F9, TRUE, 'C', '\\', 'f', 'p', }, 945 { VK_F10, TRUE, 'D', ']', 'g', 'q', }, 946 { VK_F11, TRUE, '\205', '\207', '\211', '\213', }, 947 { VK_F12, TRUE, '\206', '\210', '\212', '\214', }, 948 949 { VK_HOME, TRUE, 'G', '\302', 'w', '\303', }, 950 { VK_UP, TRUE, 'H', '\304', '\305', '\306', }, 951 { VK_PRIOR, TRUE, 'I', '\307', '\204', '\310', }, // PgUp 952 { VK_LEFT, TRUE, 'K', '\311', 's', '\312', }, 953 { VK_RIGHT, TRUE, 'M', '\313', 't', '\314', }, 954 { VK_END, TRUE, 'O', '\315', 'u', '\316', }, 955 { VK_DOWN, TRUE, 'P', '\317', '\320', '\321', }, 956 { VK_NEXT, TRUE, 'Q', '\322', 'v', '\323', }, // PgDn 957 { VK_INSERT,TRUE, 'R', '\324', '\325', '\326', }, 958 { VK_DELETE,TRUE, 'S', '\327', '\330', '\331', }, 959 { VK_BACK, TRUE, 'x', 'y', 'z', '{', }, // Backspace 960 961 { VK_SNAPSHOT,TRUE, 0, 0, 0, 'r', }, // PrtScrn 962 963 # if 0 964 // Most people don't have F13-F20, but what the hell... 965 { VK_F13, TRUE, '\332', '\333', '\334', '\335', }, 966 { VK_F14, TRUE, '\336', '\337', '\340', '\341', }, 967 { VK_F15, TRUE, '\342', '\343', '\344', '\345', }, 968 { VK_F16, TRUE, '\346', '\347', '\350', '\351', }, 969 { VK_F17, TRUE, '\352', '\353', '\354', '\355', }, 970 { VK_F18, TRUE, '\356', '\357', '\360', '\361', }, 971 { VK_F19, TRUE, '\362', '\363', '\364', '\365', }, 972 { VK_F20, TRUE, '\366', '\367', '\370', '\371', }, 973 # endif 974 { VK_ADD, TRUE, 'N', 'N', 'N', 'N', }, // keyp '+' 975 { VK_SUBTRACT, TRUE,'J', 'J', 'J', 'J', }, // keyp '-' 976 // { VK_DIVIDE, TRUE,'N', 'N', 'N', 'N', }, // keyp '/' 977 { VK_MULTIPLY, TRUE,'7', '7', '7', '7', }, // keyp '*' 978 979 { VK_NUMPAD0,TRUE, '\332', '\333', '\334', '\335', }, 980 { VK_NUMPAD1,TRUE, '\336', '\337', '\340', '\341', }, 981 { VK_NUMPAD2,TRUE, '\342', '\343', '\344', '\345', }, 982 { VK_NUMPAD3,TRUE, '\346', '\347', '\350', '\351', }, 983 { VK_NUMPAD4,TRUE, '\352', '\353', '\354', '\355', }, 984 { VK_NUMPAD5,TRUE, '\356', '\357', '\360', '\361', }, 985 { VK_NUMPAD6,TRUE, '\362', '\363', '\364', '\365', }, 986 { VK_NUMPAD7,TRUE, '\366', '\367', '\370', '\371', }, 987 { VK_NUMPAD8,TRUE, '\372', '\373', '\374', '\375', }, 988 // Sorry, out of number space! <negri> 989 { VK_NUMPAD9,TRUE, '\376', '\377', '|', '}', }, 990 }; 991 992 993 /* 994 * The return code indicates key code size. 995 */ 996 static int 997 win32_kbd_patch_key( 998 KEY_EVENT_RECORD *pker) 999 { 1000 UINT uMods = pker->dwControlKeyState; 1001 static int s_iIsDead = 0; 1002 static WORD awAnsiCode[2]; 1003 static BYTE abKeystate[256]; 1004 1005 1006 if (s_iIsDead == 2) 1007 { 1008 pker->UChar = (WCHAR) awAnsiCode[1]; 1009 s_iIsDead = 0; 1010 return 1; 1011 } 1012 1013 if (pker->UChar != 0) 1014 return 1; 1015 1016 CLEAR_FIELD(abKeystate); 1017 1018 // Clear any pending dead keys 1019 ToUnicode(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 2, 0); 1020 1021 if (uMods & SHIFT_PRESSED) 1022 abKeystate[VK_SHIFT] = 0x80; 1023 if (uMods & CAPSLOCK_ON) 1024 abKeystate[VK_CAPITAL] = 1; 1025 1026 if ((uMods & ALT_GR) == ALT_GR) 1027 { 1028 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] = 1029 abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80; 1030 } 1031 1032 s_iIsDead = ToUnicode(pker->wVirtualKeyCode, pker->wVirtualScanCode, 1033 abKeystate, awAnsiCode, 2, 0); 1034 1035 if (s_iIsDead > 0) 1036 pker->UChar = (WCHAR) awAnsiCode[0]; 1037 1038 return s_iIsDead; 1039 } 1040 1041 static BOOL g_fJustGotFocus = FALSE; 1042 1043 /* 1044 * Decode a KEY_EVENT into one or two keystrokes 1045 */ 1046 static BOOL 1047 decode_key_event( 1048 KEY_EVENT_RECORD *pker, 1049 WCHAR *pch, 1050 WCHAR *pch2, 1051 int *pmodifiers, 1052 BOOL fDoPost UNUSED) 1053 { 1054 int i; 1055 const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL); 1056 1057 *pch = *pch2 = NUL; 1058 g_fJustGotFocus = FALSE; 1059 1060 // ignore key up events 1061 if (!pker->bKeyDown) 1062 return FALSE; 1063 1064 // ignore some keystrokes 1065 switch (pker->wVirtualKeyCode) 1066 { 1067 // modifiers 1068 case VK_SHIFT: 1069 case VK_CONTROL: 1070 case VK_MENU: // Alt key 1071 return FALSE; 1072 1073 default: 1074 break; 1075 } 1076 1077 // special cases 1078 if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->UChar == NUL) 1079 { 1080 // Ctrl-6 is Ctrl-^ 1081 if (pker->wVirtualKeyCode == '6') 1082 { 1083 *pch = Ctrl_HAT; 1084 return TRUE; 1085 } 1086 // Ctrl-2 is Ctrl-@ 1087 else if (pker->wVirtualKeyCode == '2') 1088 { 1089 *pch = NUL; 1090 return TRUE; 1091 } 1092 // Ctrl-- is Ctrl-_ 1093 else if (pker->wVirtualKeyCode == 0xBD) 1094 { 1095 *pch = Ctrl__; 1096 return TRUE; 1097 } 1098 } 1099 1100 // Shift-TAB 1101 if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED)) 1102 { 1103 *pch = K_NUL; 1104 *pch2 = '\017'; 1105 return TRUE; 1106 } 1107 1108 for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]); --i >= 0; ) 1109 { 1110 if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode) 1111 { 1112 if (nModifs == 0) 1113 *pch = VirtKeyMap[i].chAlone; 1114 else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0) 1115 *pch = VirtKeyMap[i].chShift; 1116 else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0) 1117 *pch = VirtKeyMap[i].chCtrl; 1118 else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0) 1119 *pch = VirtKeyMap[i].chAlt; 1120 1121 if (*pch != 0) 1122 { 1123 if (VirtKeyMap[i].fAnsiKey) 1124 { 1125 *pch2 = *pch; 1126 *pch = K_NUL; 1127 } 1128 1129 return TRUE; 1130 } 1131 } 1132 } 1133 1134 i = win32_kbd_patch_key(pker); 1135 1136 if (i < 0) 1137 *pch = NUL; 1138 else 1139 { 1140 *pch = (i > 0) ? pker->UChar : NUL; 1141 1142 if (pmodifiers != NULL) 1143 { 1144 // Pass on the ALT key as a modifier, but only when not combined 1145 // with CTRL (which is ALTGR). 1146 if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0) 1147 *pmodifiers |= MOD_MASK_ALT; 1148 1149 // Pass on SHIFT only for special keys, because we don't know when 1150 // it's already included with the character. 1151 if ((nModifs & SHIFT) != 0 && *pch <= 0x20) 1152 *pmodifiers |= MOD_MASK_SHIFT; 1153 1154 // Pass on CTRL only for non-special keys, because we don't know 1155 // when it's already included with the character. And not when 1156 // combined with ALT (which is ALTGR). 1157 if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0 1158 && *pch >= 0x20 && *pch < 0x80) 1159 *pmodifiers |= MOD_MASK_CTRL; 1160 } 1161 } 1162 1163 return (*pch != NUL); 1164 } 1165 1166 #endif // FEAT_GUI_MSWIN 1167 1168 1169 /* 1170 * For the GUI the mouse handling is in gui_w32.c. 1171 */ 1172 #if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL) 1173 void 1174 mch_setmouse(int on UNUSED) 1175 { 1176 } 1177 #else 1178 static int g_fMouseAvail = FALSE; // mouse present 1179 static int g_fMouseActive = FALSE; // mouse enabled 1180 static int g_nMouseClick = -1; // mouse status 1181 static int g_xMouse; // mouse x coordinate 1182 static int g_yMouse; // mouse y coordinate 1183 1184 /* 1185 * Enable or disable mouse input 1186 */ 1187 void 1188 mch_setmouse(int on) 1189 { 1190 DWORD cmodein; 1191 1192 # ifdef VIMDLL 1193 if (gui.in_use) 1194 return; 1195 # endif 1196 if (!g_fMouseAvail) 1197 return; 1198 1199 g_fMouseActive = on; 1200 GetConsoleMode(g_hConIn, &cmodein); 1201 1202 if (g_fMouseActive) 1203 cmodein |= ENABLE_MOUSE_INPUT; 1204 else 1205 cmodein &= ~ENABLE_MOUSE_INPUT; 1206 1207 SetConsoleMode(g_hConIn, cmodein); 1208 } 1209 1210 1211 # if defined(FEAT_BEVAL_TERM) || defined(PROTO) 1212 /* 1213 * Called when 'balloonevalterm' changed. 1214 */ 1215 void 1216 mch_bevalterm_changed(void) 1217 { 1218 mch_setmouse(g_fMouseActive); 1219 } 1220 # endif 1221 1222 /* 1223 * Decode a MOUSE_EVENT. If it's a valid event, return MOUSE_LEFT, 1224 * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse 1225 * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG 1226 * or a MOUSE_LEFT, _MIDDLE, or _RIGHT. We encode the button type, 1227 * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick, 1228 * and we return the mouse position in g_xMouse and g_yMouse. 1229 * 1230 * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more 1231 * MOUSE_DRAGs and one MOUSE_RELEASE. MOUSE_RELEASE will be followed only 1232 * by MOUSE_LEFT, _MIDDLE, or _RIGHT. 1233 * 1234 * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE, 1235 * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, .... 1236 * 1237 * Windows will send us MOUSE_MOVED notifications whenever the mouse 1238 * moves, even if it stays within the same character cell. We ignore 1239 * all MOUSE_MOVED messages if the position hasn't really changed, and 1240 * we ignore all MOUSE_MOVED messages where no button is held down (i.e., 1241 * we're only interested in MOUSE_DRAG). 1242 * 1243 * All of this is complicated by the code that fakes MOUSE_MIDDLE on 1244 * 2-button mouses by pressing the left & right buttons simultaneously. 1245 * In practice, it's almost impossible to click both at the same time, 1246 * so we need to delay a little. Also, we tend not to get MOUSE_RELEASE 1247 * in such cases, if the user is clicking quickly. 1248 */ 1249 static BOOL 1250 decode_mouse_event( 1251 MOUSE_EVENT_RECORD *pmer) 1252 { 1253 static int s_nOldButton = -1; 1254 static int s_nOldMouseClick = -1; 1255 static int s_xOldMouse = -1; 1256 static int s_yOldMouse = -1; 1257 static linenr_T s_old_topline = 0; 1258 # ifdef FEAT_DIFF 1259 static int s_old_topfill = 0; 1260 # endif 1261 static int s_cClicks = 1; 1262 static BOOL s_fReleased = TRUE; 1263 static DWORD s_dwLastClickTime = 0; 1264 static BOOL s_fNextIsMiddle = FALSE; 1265 1266 static DWORD cButtons = 0; // number of buttons supported 1267 1268 const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED; 1269 const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED; 1270 const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED; 1271 const DWORD LEFT_RIGHT = LEFT | RIGHT; 1272 1273 int nButton; 1274 1275 if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons)) 1276 cButtons = 2; 1277 1278 if (!g_fMouseAvail || !g_fMouseActive) 1279 { 1280 g_nMouseClick = -1; 1281 return FALSE; 1282 } 1283 1284 // get a spurious MOUSE_EVENT immediately after receiving focus; ignore 1285 if (g_fJustGotFocus) 1286 { 1287 g_fJustGotFocus = FALSE; 1288 return FALSE; 1289 } 1290 1291 // unprocessed mouse click? 1292 if (g_nMouseClick != -1) 1293 return TRUE; 1294 1295 nButton = -1; 1296 g_xMouse = pmer->dwMousePosition.X; 1297 g_yMouse = pmer->dwMousePosition.Y; 1298 1299 if (pmer->dwEventFlags == MOUSE_MOVED) 1300 { 1301 // Ignore MOUSE_MOVED events if (x, y) hasn't changed. (We get these 1302 // events even when the mouse moves only within a char cell.) 1303 if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse) 1304 return FALSE; 1305 } 1306 1307 // If no buttons are pressed... 1308 if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0) 1309 { 1310 nButton = MOUSE_RELEASE; 1311 1312 // If the last thing returned was MOUSE_RELEASE, ignore this 1313 if (s_fReleased) 1314 { 1315 # ifdef FEAT_BEVAL_TERM 1316 // do return mouse move events when we want them 1317 if (p_bevalterm) 1318 nButton = MOUSE_DRAG; 1319 else 1320 # endif 1321 return FALSE; 1322 } 1323 1324 s_fReleased = TRUE; 1325 } 1326 else // one or more buttons pressed 1327 { 1328 // on a 2-button mouse, hold down left and right buttons 1329 // simultaneously to get MIDDLE. 1330 1331 if (cButtons == 2 && s_nOldButton != MOUSE_DRAG) 1332 { 1333 DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT); 1334 1335 // if either left or right button only is pressed, see if the 1336 // next mouse event has both of them pressed 1337 if (dwLR == LEFT || dwLR == RIGHT) 1338 { 1339 for (;;) 1340 { 1341 // wait a short time for next input event 1342 if (WaitForSingleObject(g_hConIn, p_mouset / 3) 1343 != WAIT_OBJECT_0) 1344 break; 1345 else 1346 { 1347 DWORD cRecords = 0; 1348 INPUT_RECORD ir; 1349 MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent; 1350 1351 peek_console_input(g_hConIn, &ir, 1, &cRecords); 1352 1353 if (cRecords == 0 || ir.EventType != MOUSE_EVENT 1354 || !(pmer2->dwButtonState & LEFT_RIGHT)) 1355 break; 1356 else 1357 { 1358 if (pmer2->dwEventFlags != MOUSE_MOVED) 1359 { 1360 read_console_input(g_hConIn, &ir, 1, &cRecords); 1361 1362 return decode_mouse_event(pmer2); 1363 } 1364 else if (s_xOldMouse == pmer2->dwMousePosition.X && 1365 s_yOldMouse == pmer2->dwMousePosition.Y) 1366 { 1367 // throw away spurious mouse move 1368 read_console_input(g_hConIn, &ir, 1, &cRecords); 1369 1370 // are there any more mouse events in queue? 1371 peek_console_input(g_hConIn, &ir, 1, &cRecords); 1372 1373 if (cRecords==0 || ir.EventType != MOUSE_EVENT) 1374 break; 1375 } 1376 else 1377 break; 1378 } 1379 } 1380 } 1381 } 1382 } 1383 1384 if (s_fNextIsMiddle) 1385 { 1386 nButton = (pmer->dwEventFlags == MOUSE_MOVED) 1387 ? MOUSE_DRAG : MOUSE_MIDDLE; 1388 s_fNextIsMiddle = FALSE; 1389 } 1390 else if (cButtons == 2 && 1391 ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT)) 1392 { 1393 nButton = MOUSE_MIDDLE; 1394 1395 if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED) 1396 { 1397 s_fNextIsMiddle = TRUE; 1398 nButton = MOUSE_RELEASE; 1399 } 1400 } 1401 else if ((pmer->dwButtonState & LEFT) == LEFT) 1402 nButton = MOUSE_LEFT; 1403 else if ((pmer->dwButtonState & MIDDLE) == MIDDLE) 1404 nButton = MOUSE_MIDDLE; 1405 else if ((pmer->dwButtonState & RIGHT) == RIGHT) 1406 nButton = MOUSE_RIGHT; 1407 1408 if (! s_fReleased && ! s_fNextIsMiddle 1409 && nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG) 1410 return FALSE; 1411 1412 s_fReleased = s_fNextIsMiddle; 1413 } 1414 1415 if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK) 1416 { 1417 // button pressed or released, without mouse moving 1418 if (nButton != -1 && nButton != MOUSE_RELEASE) 1419 { 1420 DWORD dwCurrentTime = GetTickCount(); 1421 1422 if (s_xOldMouse != g_xMouse 1423 || s_yOldMouse != g_yMouse 1424 || s_nOldButton != nButton 1425 || s_old_topline != curwin->w_topline 1426 # ifdef FEAT_DIFF 1427 || s_old_topfill != curwin->w_topfill 1428 # endif 1429 || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset) 1430 { 1431 s_cClicks = 1; 1432 } 1433 else if (++s_cClicks > 4) 1434 { 1435 s_cClicks = 1; 1436 } 1437 1438 s_dwLastClickTime = dwCurrentTime; 1439 } 1440 } 1441 else if (pmer->dwEventFlags == MOUSE_MOVED) 1442 { 1443 if (nButton != -1 && nButton != MOUSE_RELEASE) 1444 nButton = MOUSE_DRAG; 1445 1446 s_cClicks = 1; 1447 } 1448 1449 if (nButton == -1) 1450 return FALSE; 1451 1452 if (nButton != MOUSE_RELEASE) 1453 s_nOldButton = nButton; 1454 1455 g_nMouseClick = nButton; 1456 1457 if (pmer->dwControlKeyState & SHIFT_PRESSED) 1458 g_nMouseClick |= MOUSE_SHIFT; 1459 if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) 1460 g_nMouseClick |= MOUSE_CTRL; 1461 if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) 1462 g_nMouseClick |= MOUSE_ALT; 1463 1464 if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE) 1465 SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks); 1466 1467 // only pass on interesting (i.e., different) mouse events 1468 if (s_xOldMouse == g_xMouse 1469 && s_yOldMouse == g_yMouse 1470 && s_nOldMouseClick == g_nMouseClick) 1471 { 1472 g_nMouseClick = -1; 1473 return FALSE; 1474 } 1475 1476 s_xOldMouse = g_xMouse; 1477 s_yOldMouse = g_yMouse; 1478 s_old_topline = curwin->w_topline; 1479 # ifdef FEAT_DIFF 1480 s_old_topfill = curwin->w_topfill; 1481 # endif 1482 s_nOldMouseClick = g_nMouseClick; 1483 1484 return TRUE; 1485 } 1486 1487 #endif // FEAT_GUI_MSWIN 1488 1489 1490 #ifdef MCH_CURSOR_SHAPE 1491 /* 1492 * Set the shape of the cursor. 1493 * 'thickness' can be from 1 (thin) to 99 (block) 1494 */ 1495 static void 1496 mch_set_cursor_shape(int thickness) 1497 { 1498 CONSOLE_CURSOR_INFO ConsoleCursorInfo; 1499 ConsoleCursorInfo.dwSize = thickness; 1500 ConsoleCursorInfo.bVisible = s_cursor_visible; 1501 1502 SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo); 1503 if (s_cursor_visible) 1504 SetConsoleCursorPosition(g_hConOut, g_coord); 1505 } 1506 1507 void 1508 mch_update_cursor(void) 1509 { 1510 int idx; 1511 int thickness; 1512 1513 # ifdef VIMDLL 1514 if (gui.in_use) 1515 return; 1516 # endif 1517 1518 /* 1519 * How the cursor is drawn depends on the current mode. 1520 */ 1521 idx = get_shape_idx(FALSE); 1522 1523 if (shape_table[idx].shape == SHAPE_BLOCK) 1524 thickness = 99; // 100 doesn't work on W95 1525 else 1526 thickness = shape_table[idx].percentage; 1527 mch_set_cursor_shape(thickness); 1528 } 1529 #endif 1530 1531 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 1532 /* 1533 * Handle FOCUS_EVENT. 1534 */ 1535 static void 1536 handle_focus_event(INPUT_RECORD ir) 1537 { 1538 g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus; 1539 ui_focus_change((int)g_fJustGotFocus); 1540 } 1541 1542 static void ResizeConBuf(HANDLE hConsole, COORD coordScreen); 1543 1544 /* 1545 * Wait until console input from keyboard or mouse is available, 1546 * or the time is up. 1547 * When "ignore_input" is TRUE even wait when input is available. 1548 * Return TRUE if something is available FALSE if not. 1549 */ 1550 static int 1551 WaitForChar(long msec, int ignore_input) 1552 { 1553 DWORD dwNow = 0, dwEndTime = 0; 1554 INPUT_RECORD ir; 1555 DWORD cRecords; 1556 WCHAR ch, ch2; 1557 # ifdef FEAT_TIMERS 1558 int tb_change_cnt = typebuf.tb_change_cnt; 1559 # endif 1560 1561 if (msec > 0) 1562 // Wait until the specified time has elapsed. 1563 dwEndTime = GetTickCount() + msec; 1564 else if (msec < 0) 1565 // Wait forever. 1566 dwEndTime = INFINITE; 1567 1568 // We need to loop until the end of the time period, because 1569 // we might get multiple unusable mouse events in that time. 1570 for (;;) 1571 { 1572 // Only process messages when waiting. 1573 if (msec != 0) 1574 { 1575 # ifdef MESSAGE_QUEUE 1576 parse_queued_messages(); 1577 # endif 1578 # ifdef FEAT_MZSCHEME 1579 mzvim_check_threads(); 1580 # endif 1581 # ifdef FEAT_CLIENTSERVER 1582 serverProcessPendingMessages(); 1583 # endif 1584 } 1585 1586 if (g_nMouseClick != -1 1587 # ifdef FEAT_CLIENTSERVER 1588 || (!ignore_input && input_available()) 1589 # endif 1590 ) 1591 return TRUE; 1592 1593 if (msec > 0) 1594 { 1595 // If the specified wait time has passed, return. Beware that 1596 // GetTickCount() may wrap around (overflow). 1597 dwNow = GetTickCount(); 1598 if ((int)(dwNow - dwEndTime) >= 0) 1599 break; 1600 } 1601 if (msec != 0) 1602 { 1603 DWORD dwWaitTime = dwEndTime - dwNow; 1604 1605 // Don't wait for more than 11 msec to avoid dropping characters, 1606 // check channel while waiting for input and handle a callback from 1607 // 'balloonexpr'. 1608 if (dwWaitTime > 11) 1609 dwWaitTime = 11; 1610 1611 # ifdef FEAT_MZSCHEME 1612 if (mzthreads_allowed() && p_mzq > 0 && (long)dwWaitTime > p_mzq) 1613 dwWaitTime = p_mzq; // don't wait longer than 'mzquantum' 1614 # endif 1615 # ifdef FEAT_TIMERS 1616 // When waiting very briefly don't trigger timers. 1617 if (dwWaitTime > 10) 1618 { 1619 long due_time; 1620 1621 // Trigger timers and then get the time in msec until the next 1622 // one is due. Wait up to that time. 1623 due_time = check_due_timer(); 1624 if (typebuf.tb_change_cnt != tb_change_cnt) 1625 { 1626 // timer may have used feedkeys(). 1627 return FALSE; 1628 } 1629 if (due_time > 0 && dwWaitTime > (DWORD)due_time) 1630 dwWaitTime = due_time; 1631 } 1632 # endif 1633 if ( 1634 # ifdef FEAT_CLIENTSERVER 1635 // Wait for either an event on the console input or a 1636 // message in the client-server window. 1637 msg_wait_for_multiple_objects(1, &g_hConIn, FALSE, 1638 dwWaitTime, QS_SENDMESSAGE) != WAIT_OBJECT_0 1639 # else 1640 wait_for_single_object(g_hConIn, dwWaitTime) 1641 != WAIT_OBJECT_0 1642 # endif 1643 ) 1644 continue; 1645 } 1646 1647 cRecords = 0; 1648 peek_console_input(g_hConIn, &ir, 1, &cRecords); 1649 1650 # ifdef FEAT_MBYTE_IME 1651 if (State & CMDLINE && msg_row == Rows - 1) 1652 { 1653 CONSOLE_SCREEN_BUFFER_INFO csbi; 1654 1655 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi)) 1656 { 1657 if (csbi.dwCursorPosition.Y != msg_row) 1658 { 1659 // The screen is now messed up, must redraw the command 1660 // line and later all the windows. 1661 redraw_all_later(CLEAR); 1662 compute_cmdrow(); 1663 redrawcmd(); 1664 } 1665 } 1666 } 1667 # endif 1668 1669 if (cRecords > 0) 1670 { 1671 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) 1672 { 1673 # ifdef FEAT_MBYTE_IME 1674 // Windows IME sends two '\n's with only one 'ENTER'. First: 1675 // wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 1676 if (ir.Event.KeyEvent.UChar == 0 1677 && ir.Event.KeyEvent.wVirtualKeyCode == 13) 1678 { 1679 read_console_input(g_hConIn, &ir, 1, &cRecords); 1680 continue; 1681 } 1682 # endif 1683 if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2, 1684 NULL, FALSE)) 1685 return TRUE; 1686 } 1687 1688 read_console_input(g_hConIn, &ir, 1, &cRecords); 1689 1690 if (ir.EventType == FOCUS_EVENT) 1691 handle_focus_event(ir); 1692 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT) 1693 { 1694 COORD dwSize = ir.Event.WindowBufferSizeEvent.dwSize; 1695 1696 // Only call shell_resized() when the size actually changed to 1697 // avoid the screen is cleared. 1698 if (dwSize.X != Columns || dwSize.Y != Rows) 1699 { 1700 CONSOLE_SCREEN_BUFFER_INFO csbi; 1701 GetConsoleScreenBufferInfo(g_hConOut, &csbi); 1702 dwSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1; 1703 dwSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; 1704 if (dwSize.X != Columns || dwSize.Y != Rows) 1705 { 1706 ResizeConBuf(g_hConOut, dwSize); 1707 shell_resized(); 1708 } 1709 } 1710 } 1711 else if (ir.EventType == MOUSE_EVENT 1712 && decode_mouse_event(&ir.Event.MouseEvent)) 1713 return TRUE; 1714 } 1715 else if (msec == 0) 1716 break; 1717 } 1718 1719 # ifdef FEAT_CLIENTSERVER 1720 // Something might have been received while we were waiting. 1721 if (input_available()) 1722 return TRUE; 1723 # endif 1724 1725 return FALSE; 1726 } 1727 1728 /* 1729 * return non-zero if a character is available 1730 */ 1731 int 1732 mch_char_avail(void) 1733 { 1734 # ifdef VIMDLL 1735 if (gui.in_use) 1736 return TRUE; 1737 # endif 1738 return WaitForChar(0L, FALSE); 1739 } 1740 1741 # if defined(FEAT_TERMINAL) || defined(PROTO) 1742 /* 1743 * Check for any pending input or messages. 1744 */ 1745 int 1746 mch_check_messages(void) 1747 { 1748 # ifdef VIMDLL 1749 if (gui.in_use) 1750 return TRUE; 1751 # endif 1752 return WaitForChar(0L, TRUE); 1753 } 1754 # endif 1755 1756 /* 1757 * Create the console input. Used when reading stdin doesn't work. 1758 */ 1759 static void 1760 create_conin(void) 1761 { 1762 g_hConIn = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE, 1763 FILE_SHARE_READ|FILE_SHARE_WRITE, 1764 (LPSECURITY_ATTRIBUTES) NULL, 1765 OPEN_EXISTING, 0, (HANDLE)NULL); 1766 did_create_conin = TRUE; 1767 } 1768 1769 /* 1770 * Get a keystroke or a mouse event, use a blocking wait. 1771 */ 1772 static WCHAR 1773 tgetch(int *pmodifiers, WCHAR *pch2) 1774 { 1775 WCHAR ch; 1776 1777 for (;;) 1778 { 1779 INPUT_RECORD ir; 1780 DWORD cRecords = 0; 1781 1782 # ifdef FEAT_CLIENTSERVER 1783 (void)WaitForChar(-1L, FALSE); 1784 if (input_available()) 1785 return 0; 1786 if (g_nMouseClick != -1) 1787 return 0; 1788 # endif 1789 if (read_console_input(g_hConIn, &ir, 1, &cRecords) == 0) 1790 { 1791 if (did_create_conin) 1792 read_error_exit(); 1793 create_conin(); 1794 continue; 1795 } 1796 1797 if (ir.EventType == KEY_EVENT) 1798 { 1799 if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2, 1800 pmodifiers, TRUE)) 1801 return ch; 1802 } 1803 else if (ir.EventType == FOCUS_EVENT) 1804 handle_focus_event(ir); 1805 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT) 1806 shell_resized(); 1807 else if (ir.EventType == MOUSE_EVENT) 1808 { 1809 if (decode_mouse_event(&ir.Event.MouseEvent)) 1810 return 0; 1811 } 1812 } 1813 } 1814 #endif // !FEAT_GUI_MSWIN 1815 1816 1817 /* 1818 * mch_inchar(): low-level input function. 1819 * Get one or more characters from the keyboard or the mouse. 1820 * If time == 0, do not wait for characters. 1821 * If time == n, wait a short time for characters. 1822 * If time == -1, wait forever for characters. 1823 * Returns the number of characters read into buf. 1824 */ 1825 int 1826 mch_inchar( 1827 char_u *buf UNUSED, 1828 int maxlen UNUSED, 1829 long time UNUSED, 1830 int tb_change_cnt UNUSED) 1831 { 1832 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 1833 1834 int len; 1835 int c; 1836 # ifdef VIMDLL 1837 // Extra space for maximum three CSIs. E.g. U+1B6DB -> 0xF0 0x9B 0x9B 0x9B. 1838 # define TYPEAHEADSPACE 6 1839 # else 1840 # define TYPEAHEADSPACE 0 1841 # endif 1842 # define TYPEAHEADLEN (20 + TYPEAHEADSPACE) 1843 static char_u typeahead[TYPEAHEADLEN]; // previously typed bytes. 1844 static int typeaheadlen = 0; 1845 1846 # ifdef VIMDLL 1847 if (gui.in_use) 1848 return 0; 1849 # endif 1850 1851 // First use any typeahead that was kept because "buf" was too small. 1852 if (typeaheadlen > 0) 1853 goto theend; 1854 1855 if (time >= 0) 1856 { 1857 if (!WaitForChar(time, FALSE)) // no character available 1858 return 0; 1859 } 1860 else // time == -1, wait forever 1861 { 1862 mch_set_winsize_now(); // Allow winsize changes from now on 1863 1864 /* 1865 * If there is no character available within 2 seconds (default) 1866 * write the autoscript file to disk. Or cause the CursorHold event 1867 * to be triggered. 1868 */ 1869 if (!WaitForChar(p_ut, FALSE)) 1870 { 1871 if (trigger_cursorhold() && maxlen >= 3) 1872 { 1873 buf[0] = K_SPECIAL; 1874 buf[1] = KS_EXTRA; 1875 buf[2] = (int)KE_CURSORHOLD; 1876 return 3; 1877 } 1878 before_blocking(); 1879 } 1880 } 1881 1882 /* 1883 * Try to read as many characters as there are, until the buffer is full. 1884 */ 1885 1886 // we will get at least one key. Get more if they are available. 1887 g_fCBrkPressed = FALSE; 1888 1889 # ifdef MCH_WRITE_DUMP 1890 if (fdDump) 1891 fputc('[', fdDump); 1892 # endif 1893 1894 // Keep looping until there is something in the typeahead buffer and more 1895 // to get and still room in the buffer (up to two bytes for a char and 1896 // three bytes for a modifier). 1897 while ((typeaheadlen == 0 || WaitForChar(0L, FALSE)) 1898 && typeaheadlen + 5 + TYPEAHEADSPACE <= TYPEAHEADLEN) 1899 { 1900 if (typebuf_changed(tb_change_cnt)) 1901 { 1902 // "buf" may be invalid now if a client put something in the 1903 // typeahead buffer and "buf" is in the typeahead buffer. 1904 typeaheadlen = 0; 1905 break; 1906 } 1907 if (g_nMouseClick != -1) 1908 { 1909 # ifdef MCH_WRITE_DUMP 1910 if (fdDump) 1911 fprintf(fdDump, "{%02x @ %d, %d}", 1912 g_nMouseClick, g_xMouse, g_yMouse); 1913 # endif 1914 typeahead[typeaheadlen++] = ESC + 128; 1915 typeahead[typeaheadlen++] = 'M'; 1916 typeahead[typeaheadlen++] = g_nMouseClick; 1917 typeahead[typeaheadlen++] = g_xMouse + '!'; 1918 typeahead[typeaheadlen++] = g_yMouse + '!'; 1919 g_nMouseClick = -1; 1920 } 1921 else 1922 { 1923 WCHAR ch2 = NUL; 1924 int modifiers = 0; 1925 1926 c = tgetch(&modifiers, &ch2); 1927 1928 if (typebuf_changed(tb_change_cnt)) 1929 { 1930 // "buf" may be invalid now if a client put something in the 1931 // typeahead buffer and "buf" is in the typeahead buffer. 1932 typeaheadlen = 0; 1933 break; 1934 } 1935 1936 if (c == Ctrl_C && ctrl_c_interrupts) 1937 { 1938 # if defined(FEAT_CLIENTSERVER) 1939 trash_input_buf(); 1940 # endif 1941 got_int = TRUE; 1942 } 1943 1944 if (g_nMouseClick == -1) 1945 { 1946 int n = 1; 1947 1948 if (ch2 == NUL) 1949 { 1950 int i, j; 1951 char_u *p; 1952 WCHAR ch[2]; 1953 1954 ch[0] = c; 1955 if (c >= 0xD800 && c <= 0xDBFF) // High surrogate 1956 { 1957 ch[1] = tgetch(&modifiers, &ch2); 1958 n++; 1959 } 1960 p = utf16_to_enc(ch, &n); 1961 if (p != NULL) 1962 { 1963 for (i = 0, j = 0; i < n; i++) 1964 { 1965 typeahead[typeaheadlen + j++] = p[i]; 1966 # ifdef VIMDLL 1967 if (p[i] == CSI) 1968 { 1969 typeahead[typeaheadlen + j++] = KS_EXTRA; 1970 typeahead[typeaheadlen + j++] = KE_CSI; 1971 } 1972 # endif 1973 } 1974 n = j; 1975 vim_free(p); 1976 } 1977 } 1978 else 1979 { 1980 typeahead[typeaheadlen] = c; 1981 # ifdef VIMDLL 1982 if (c == CSI) 1983 { 1984 typeahead[typeaheadlen + 1] = KS_EXTRA; 1985 typeahead[typeaheadlen + 2] = KE_CSI; 1986 n = 3; 1987 } 1988 # endif 1989 } 1990 if (ch2 != NUL) 1991 { 1992 if (c == K_NUL) 1993 { 1994 switch (ch2) 1995 { 1996 case (WCHAR)'\324': // SHIFT+Insert 1997 case (WCHAR)'\325': // CTRL+Insert 1998 case (WCHAR)'\327': // SHIFT+Delete 1999 case (WCHAR)'\330': // CTRL+Delete 2000 typeahead[typeaheadlen + n] = (char_u)ch2; 2001 n++; 2002 break; 2003 2004 default: 2005 typeahead[typeaheadlen + n] = 3; 2006 typeahead[typeaheadlen + n + 1] = (char_u)ch2; 2007 n += 2; 2008 break; 2009 } 2010 } 2011 else 2012 { 2013 typeahead[typeaheadlen + n] = 3; 2014 typeahead[typeaheadlen + n + 1] = (char_u)ch2; 2015 n += 2; 2016 } 2017 } 2018 2019 // Use the ALT key to set the 8th bit of the character 2020 // when it's one byte, the 8th bit isn't set yet and not 2021 // using a double-byte encoding (would become a lead 2022 // byte). 2023 if ((modifiers & MOD_MASK_ALT) 2024 && n == 1 2025 && (typeahead[typeaheadlen] & 0x80) == 0 2026 && !enc_dbcs 2027 ) 2028 { 2029 n = (*mb_char2bytes)(typeahead[typeaheadlen] | 0x80, 2030 typeahead + typeaheadlen); 2031 modifiers &= ~MOD_MASK_ALT; 2032 } 2033 2034 if (modifiers != 0) 2035 { 2036 // Prepend modifiers to the character. 2037 mch_memmove(typeahead + typeaheadlen + 3, 2038 typeahead + typeaheadlen, n); 2039 typeahead[typeaheadlen++] = K_SPECIAL; 2040 typeahead[typeaheadlen++] = (char_u)KS_MODIFIER; 2041 typeahead[typeaheadlen++] = modifiers; 2042 } 2043 2044 typeaheadlen += n; 2045 2046 # ifdef MCH_WRITE_DUMP 2047 if (fdDump) 2048 fputc(c, fdDump); 2049 # endif 2050 } 2051 } 2052 } 2053 2054 # ifdef MCH_WRITE_DUMP 2055 if (fdDump) 2056 { 2057 fputs("]\n", fdDump); 2058 fflush(fdDump); 2059 } 2060 # endif 2061 2062 theend: 2063 // Move typeahead to "buf", as much as fits. 2064 len = 0; 2065 while (len < maxlen && typeaheadlen > 0) 2066 { 2067 buf[len++] = typeahead[0]; 2068 mch_memmove(typeahead, typeahead + 1, --typeaheadlen); 2069 } 2070 # ifdef FEAT_JOB_CHANNEL 2071 if (len > 0) 2072 { 2073 buf[len] = NUL; 2074 ch_log(NULL, "raw key input: \"%s\"", buf); 2075 } 2076 # endif 2077 return len; 2078 2079 #else // FEAT_GUI_MSWIN 2080 return 0; 2081 #endif // FEAT_GUI_MSWIN 2082 } 2083 2084 #ifndef PROTO 2085 # ifndef __MINGW32__ 2086 # include <shellapi.h> // required for FindExecutable() 2087 # endif 2088 #endif 2089 2090 /* 2091 * Return TRUE if "name" is an executable file, FALSE if not or it doesn't exist. 2092 * When returning TRUE and "path" is not NULL save the path and set "*path" to 2093 * the allocated memory. 2094 * TODO: Should somehow check if it's really executable. 2095 */ 2096 static int 2097 executable_file(char *name, char_u **path) 2098 { 2099 if (mch_getperm((char_u *)name) != -1 && !mch_isdir((char_u *)name)) 2100 { 2101 if (path != NULL) 2102 *path = FullName_save((char_u *)name, FALSE); 2103 return TRUE; 2104 } 2105 return FALSE; 2106 } 2107 2108 /* 2109 * If "use_path" is TRUE: Return TRUE if "name" is in $PATH. 2110 * If "use_path" is FALSE: Return TRUE if "name" exists. 2111 * If "use_pathext" is TRUE search "name" with extensions in $PATHEXT. 2112 * When returning TRUE and "path" is not NULL save the path and set "*path" to 2113 * the allocated memory. 2114 */ 2115 static int 2116 executable_exists(char *name, char_u **path, int use_path, int use_pathext) 2117 { 2118 // WinNT and later can use _MAX_PATH wide characters for a pathname, which 2119 // means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is 2120 // UTF-8. 2121 char_u buf[_MAX_PATH * 3]; 2122 size_t len = STRLEN(name); 2123 size_t tmplen; 2124 char_u *p, *e, *e2; 2125 char_u *pathbuf = NULL; 2126 char_u *pathext = NULL; 2127 char_u *pathextbuf = NULL; 2128 int noext = FALSE; 2129 int retval = FALSE; 2130 2131 if (len >= sizeof(buf)) // safety check 2132 return FALSE; 2133 2134 // Using the name directly when a Unix-shell like 'shell'. 2135 if (strstr((char *)gettail(p_sh), "sh") != NULL) 2136 noext = TRUE; 2137 2138 if (use_pathext) 2139 { 2140 pathext = mch_getenv("PATHEXT"); 2141 if (pathext == NULL) 2142 pathext = (char_u *)".com;.exe;.bat;.cmd"; 2143 2144 if (noext == FALSE) 2145 { 2146 /* 2147 * Loop over all extensions in $PATHEXT. 2148 * Check "name" ends with extension. 2149 */ 2150 p = pathext; 2151 while (*p) 2152 { 2153 if (p[0] == ';' 2154 || (p[0] == '.' && (p[1] == NUL || p[1] == ';'))) 2155 { 2156 // Skip empty or single ".". 2157 ++p; 2158 continue; 2159 } 2160 e = vim_strchr(p, ';'); 2161 if (e == NULL) 2162 e = p + STRLEN(p); 2163 tmplen = e - p; 2164 2165 if (_strnicoll(name + len - tmplen, (char *)p, tmplen) == 0) 2166 { 2167 noext = TRUE; 2168 break; 2169 } 2170 2171 p = e; 2172 } 2173 } 2174 } 2175 2176 // Prepend single "." to pathext, it's means no extension added. 2177 if (pathext == NULL) 2178 pathext = (char_u *)"."; 2179 else if (noext == TRUE) 2180 { 2181 if (pathextbuf == NULL) 2182 pathextbuf = alloc(STRLEN(pathext) + 3); 2183 if (pathextbuf == NULL) 2184 { 2185 retval = FALSE; 2186 goto theend; 2187 } 2188 STRCPY(pathextbuf, ".;"); 2189 STRCAT(pathextbuf, pathext); 2190 pathext = pathextbuf; 2191 } 2192 2193 // Use $PATH when "use_path" is TRUE and "name" is basename. 2194 if (use_path && gettail((char_u *)name) == (char_u *)name) 2195 { 2196 p = mch_getenv("PATH"); 2197 if (p != NULL) 2198 { 2199 pathbuf = alloc(STRLEN(p) + 3); 2200 if (pathbuf == NULL) 2201 { 2202 retval = FALSE; 2203 goto theend; 2204 } 2205 STRCPY(pathbuf, ".;"); 2206 STRCAT(pathbuf, p); 2207 } 2208 } 2209 2210 /* 2211 * Walk through all entries in $PATH to check if "name" exists there and 2212 * is an executable file. 2213 */ 2214 p = (pathbuf != NULL) ? pathbuf : (char_u *)"."; 2215 while (*p) 2216 { 2217 if (*p == ';') // Skip empty entry 2218 { 2219 ++p; 2220 continue; 2221 } 2222 e = vim_strchr(p, ';'); 2223 if (e == NULL) 2224 e = p + STRLEN(p); 2225 2226 if (e - p + len + 2 > sizeof(buf)) 2227 { 2228 retval = FALSE; 2229 goto theend; 2230 } 2231 // A single "." that means current dir. 2232 if (e - p == 1 && *p == '.') 2233 STRCPY(buf, name); 2234 else 2235 { 2236 vim_strncpy(buf, p, e - p); 2237 add_pathsep(buf); 2238 STRCAT(buf, name); 2239 } 2240 tmplen = STRLEN(buf); 2241 2242 /* 2243 * Loop over all extensions in $PATHEXT. 2244 * Check "name" with extension added. 2245 */ 2246 p = pathext; 2247 while (*p) 2248 { 2249 if (*p == ';') 2250 { 2251 // Skip empty entry 2252 ++p; 2253 continue; 2254 } 2255 e2 = vim_strchr(p, (int)';'); 2256 if (e2 == NULL) 2257 e2 = p + STRLEN(p); 2258 2259 if (!(p[0] == '.' && (p[1] == NUL || p[1] == ';'))) 2260 { 2261 // Not a single "." that means no extension is added. 2262 if (e2 - p + tmplen + 1 > sizeof(buf)) 2263 { 2264 retval = FALSE; 2265 goto theend; 2266 } 2267 vim_strncpy(buf + tmplen, p, e2 - p); 2268 } 2269 if (executable_file((char *)buf, path)) 2270 { 2271 retval = TRUE; 2272 goto theend; 2273 } 2274 2275 p = e2; 2276 } 2277 2278 p = e; 2279 } 2280 2281 theend: 2282 free(pathextbuf); 2283 free(pathbuf); 2284 return retval; 2285 } 2286 2287 #if (defined(__MINGW32__) && __MSVCRT_VERSION__ >= 0x800) || \ 2288 (defined(_MSC_VER) && _MSC_VER >= 1400) 2289 /* 2290 * Bad parameter handler. 2291 * 2292 * Certain MS CRT functions will intentionally crash when passed invalid 2293 * parameters to highlight possible security holes. Setting this function as 2294 * the bad parameter handler will prevent the crash. 2295 * 2296 * In debug builds the parameters contain CRT information that might help track 2297 * down the source of a problem, but in non-debug builds the arguments are all 2298 * NULL/0. Debug builds will also produce assert dialogs from the CRT, it is 2299 * worth allowing these to make debugging of issues easier. 2300 */ 2301 static void 2302 bad_param_handler(const wchar_t *expression, 2303 const wchar_t *function, 2304 const wchar_t *file, 2305 unsigned int line, 2306 uintptr_t pReserved) 2307 { 2308 } 2309 2310 # define SET_INVALID_PARAM_HANDLER \ 2311 ((void)_set_invalid_parameter_handler(bad_param_handler)) 2312 #else 2313 # define SET_INVALID_PARAM_HANDLER 2314 #endif 2315 2316 #ifdef FEAT_GUI_MSWIN 2317 2318 /* 2319 * GUI version of mch_init(). 2320 */ 2321 static void 2322 mch_init_g(void) 2323 { 2324 # ifndef __MINGW32__ 2325 extern int _fmode; 2326 # endif 2327 2328 // Silently handle invalid parameters to CRT functions 2329 SET_INVALID_PARAM_HANDLER; 2330 2331 // Let critical errors result in a failure, not in a dialog box. Required 2332 // for the timestamp test to work on removed floppies. 2333 SetErrorMode(SEM_FAILCRITICALERRORS); 2334 2335 _fmode = O_BINARY; // we do our own CR-LF translation 2336 2337 // Specify window size. Is there a place to get the default from? 2338 Rows = 25; 2339 Columns = 80; 2340 2341 // Look for 'vimrun' 2342 { 2343 char_u vimrun_location[_MAX_PATH + 4]; 2344 2345 // First try in same directory as gvim.exe 2346 STRCPY(vimrun_location, exe_name); 2347 STRCPY(gettail(vimrun_location), "vimrun.exe"); 2348 if (mch_getperm(vimrun_location) >= 0) 2349 { 2350 if (*skiptowhite(vimrun_location) != NUL) 2351 { 2352 // Enclose path with white space in double quotes. 2353 mch_memmove(vimrun_location + 1, vimrun_location, 2354 STRLEN(vimrun_location) + 1); 2355 *vimrun_location = '"'; 2356 STRCPY(gettail(vimrun_location), "vimrun\" "); 2357 } 2358 else 2359 STRCPY(gettail(vimrun_location), "vimrun "); 2360 2361 vimrun_path = (char *)vim_strsave(vimrun_location); 2362 s_dont_use_vimrun = FALSE; 2363 } 2364 else if (executable_exists("vimrun.exe", NULL, TRUE, FALSE)) 2365 s_dont_use_vimrun = FALSE; 2366 2367 // Don't give the warning for a missing vimrun.exe right now, but only 2368 // when vimrun was supposed to be used. Don't bother people that do 2369 // not need vimrun.exe. 2370 if (s_dont_use_vimrun) 2371 need_vimrun_warning = TRUE; 2372 } 2373 2374 /* 2375 * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'. 2376 * Otherwise the default "findstr /n" is used. 2377 */ 2378 if (!executable_exists("findstr.exe", NULL, TRUE, FALSE)) 2379 set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0); 2380 2381 # ifdef FEAT_CLIPBOARD 2382 win_clip_init(); 2383 # endif 2384 2385 vtp_flag_init(); 2386 } 2387 2388 2389 #endif // FEAT_GUI_MSWIN 2390 2391 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 2392 2393 # define SRWIDTH(sr) ((sr).Right - (sr).Left + 1) 2394 # define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1) 2395 2396 /* 2397 * ClearConsoleBuffer() 2398 * Description: 2399 * Clears the entire contents of the console screen buffer, using the 2400 * specified attribute. 2401 * Returns: 2402 * TRUE on success 2403 */ 2404 static BOOL 2405 ClearConsoleBuffer(WORD wAttribute) 2406 { 2407 CONSOLE_SCREEN_BUFFER_INFO csbi; 2408 COORD coord; 2409 DWORD NumCells, dummy; 2410 2411 if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi)) 2412 return FALSE; 2413 2414 NumCells = csbi.dwSize.X * csbi.dwSize.Y; 2415 coord.X = 0; 2416 coord.Y = 0; 2417 if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells, 2418 coord, &dummy)) 2419 return FALSE; 2420 if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells, 2421 coord, &dummy)) 2422 return FALSE; 2423 2424 return TRUE; 2425 } 2426 2427 /* 2428 * FitConsoleWindow() 2429 * Description: 2430 * Checks if the console window will fit within given buffer dimensions. 2431 * Also, if requested, will shrink the window to fit. 2432 * Returns: 2433 * TRUE on success 2434 */ 2435 static BOOL 2436 FitConsoleWindow( 2437 COORD dwBufferSize, 2438 BOOL WantAdjust) 2439 { 2440 CONSOLE_SCREEN_BUFFER_INFO csbi; 2441 COORD dwWindowSize; 2442 BOOL NeedAdjust = FALSE; 2443 2444 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi)) 2445 { 2446 /* 2447 * A buffer resize will fail if the current console window does 2448 * not lie completely within that buffer. To avoid this, we might 2449 * have to move and possibly shrink the window. 2450 */ 2451 if (csbi.srWindow.Right >= dwBufferSize.X) 2452 { 2453 dwWindowSize.X = SRWIDTH(csbi.srWindow); 2454 if (dwWindowSize.X > dwBufferSize.X) 2455 dwWindowSize.X = dwBufferSize.X; 2456 csbi.srWindow.Right = dwBufferSize.X - 1; 2457 csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X; 2458 NeedAdjust = TRUE; 2459 } 2460 if (csbi.srWindow.Bottom >= dwBufferSize.Y) 2461 { 2462 dwWindowSize.Y = SRHEIGHT(csbi.srWindow); 2463 if (dwWindowSize.Y > dwBufferSize.Y) 2464 dwWindowSize.Y = dwBufferSize.Y; 2465 csbi.srWindow.Bottom = dwBufferSize.Y - 1; 2466 csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y; 2467 NeedAdjust = TRUE; 2468 } 2469 if (NeedAdjust && WantAdjust) 2470 { 2471 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow)) 2472 return FALSE; 2473 } 2474 return TRUE; 2475 } 2476 2477 return FALSE; 2478 } 2479 2480 typedef struct ConsoleBufferStruct 2481 { 2482 BOOL IsValid; 2483 CONSOLE_SCREEN_BUFFER_INFO Info; 2484 PCHAR_INFO Buffer; 2485 COORD BufferSize; 2486 PSMALL_RECT Regions; 2487 int NumRegions; 2488 } ConsoleBuffer; 2489 2490 /* 2491 * SaveConsoleBuffer() 2492 * Description: 2493 * Saves important information about the console buffer, including the 2494 * actual buffer contents. The saved information is suitable for later 2495 * restoration by RestoreConsoleBuffer(). 2496 * Returns: 2497 * TRUE if all information was saved; FALSE otherwise 2498 * If FALSE, still sets cb->IsValid if buffer characteristics were saved. 2499 */ 2500 static BOOL 2501 SaveConsoleBuffer( 2502 ConsoleBuffer *cb) 2503 { 2504 DWORD NumCells; 2505 COORD BufferCoord; 2506 SMALL_RECT ReadRegion; 2507 WORD Y, Y_incr; 2508 int i, numregions; 2509 2510 if (cb == NULL) 2511 return FALSE; 2512 2513 if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info)) 2514 { 2515 cb->IsValid = FALSE; 2516 return FALSE; 2517 } 2518 cb->IsValid = TRUE; 2519 2520 /* 2521 * Allocate a buffer large enough to hold the entire console screen 2522 * buffer. If this ConsoleBuffer structure has already been initialized 2523 * with a buffer of the correct size, then just use that one. 2524 */ 2525 if (!cb->IsValid || cb->Buffer == NULL || 2526 cb->BufferSize.X != cb->Info.dwSize.X || 2527 cb->BufferSize.Y != cb->Info.dwSize.Y) 2528 { 2529 cb->BufferSize.X = cb->Info.dwSize.X; 2530 cb->BufferSize.Y = cb->Info.dwSize.Y; 2531 NumCells = cb->BufferSize.X * cb->BufferSize.Y; 2532 vim_free(cb->Buffer); 2533 cb->Buffer = ALLOC_MULT(CHAR_INFO, NumCells); 2534 if (cb->Buffer == NULL) 2535 return FALSE; 2536 } 2537 2538 /* 2539 * We will now copy the console screen buffer into our buffer. 2540 * ReadConsoleOutput() seems to be limited as far as how much you 2541 * can read at a time. Empirically, this number seems to be about 2542 * 12000 cells (rows * columns). Start at position (0, 0) and copy 2543 * in chunks until it is all copied. The chunks will all have the 2544 * same horizontal characteristics, so initialize them now. The 2545 * height of each chunk will be (12000 / width). 2546 */ 2547 BufferCoord.X = 0; 2548 ReadRegion.Left = 0; 2549 ReadRegion.Right = cb->Info.dwSize.X - 1; 2550 Y_incr = 12000 / cb->Info.dwSize.X; 2551 2552 numregions = (cb->Info.dwSize.Y + Y_incr - 1) / Y_incr; 2553 if (cb->Regions == NULL || numregions != cb->NumRegions) 2554 { 2555 cb->NumRegions = numregions; 2556 vim_free(cb->Regions); 2557 cb->Regions = ALLOC_MULT(SMALL_RECT, cb->NumRegions); 2558 if (cb->Regions == NULL) 2559 { 2560 VIM_CLEAR(cb->Buffer); 2561 return FALSE; 2562 } 2563 } 2564 2565 for (i = 0, Y = 0; i < cb->NumRegions; i++, Y += Y_incr) 2566 { 2567 /* 2568 * Read into position (0, Y) in our buffer. 2569 */ 2570 BufferCoord.Y = Y; 2571 /* 2572 * Read the region whose top left corner is (0, Y) and whose bottom 2573 * right corner is (width - 1, Y + Y_incr - 1). This should define 2574 * a region of size width by Y_incr. Don't worry if this region is 2575 * too large for the remaining buffer; it will be cropped. 2576 */ 2577 ReadRegion.Top = Y; 2578 ReadRegion.Bottom = Y + Y_incr - 1; 2579 if (!ReadConsoleOutputW(g_hConOut, // output handle 2580 cb->Buffer, // our buffer 2581 cb->BufferSize, // dimensions of our buffer 2582 BufferCoord, // offset in our buffer 2583 &ReadRegion)) // region to save 2584 { 2585 VIM_CLEAR(cb->Buffer); 2586 VIM_CLEAR(cb->Regions); 2587 return FALSE; 2588 } 2589 cb->Regions[i] = ReadRegion; 2590 } 2591 2592 return TRUE; 2593 } 2594 2595 /* 2596 * RestoreConsoleBuffer() 2597 * Description: 2598 * Restores important information about the console buffer, including the 2599 * actual buffer contents, if desired. The information to restore is in 2600 * the same format used by SaveConsoleBuffer(). 2601 * Returns: 2602 * TRUE on success 2603 */ 2604 static BOOL 2605 RestoreConsoleBuffer( 2606 ConsoleBuffer *cb, 2607 BOOL RestoreScreen) 2608 { 2609 COORD BufferCoord; 2610 SMALL_RECT WriteRegion; 2611 int i; 2612 2613 if (cb == NULL || !cb->IsValid) 2614 return FALSE; 2615 2616 /* 2617 * Before restoring the buffer contents, clear the current buffer, and 2618 * restore the cursor position and window information. Doing this now 2619 * prevents old buffer contents from "flashing" onto the screen. 2620 */ 2621 if (RestoreScreen) 2622 ClearConsoleBuffer(cb->Info.wAttributes); 2623 2624 FitConsoleWindow(cb->Info.dwSize, TRUE); 2625 if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize)) 2626 return FALSE; 2627 if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes)) 2628 return FALSE; 2629 2630 if (!RestoreScreen) 2631 { 2632 /* 2633 * No need to restore the screen buffer contents, so we're done. 2634 */ 2635 return TRUE; 2636 } 2637 2638 if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition)) 2639 return FALSE; 2640 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow)) 2641 return FALSE; 2642 2643 /* 2644 * Restore the screen buffer contents. 2645 */ 2646 if (cb->Buffer != NULL) 2647 { 2648 for (i = 0; i < cb->NumRegions; i++) 2649 { 2650 BufferCoord.X = cb->Regions[i].Left; 2651 BufferCoord.Y = cb->Regions[i].Top; 2652 WriteRegion = cb->Regions[i]; 2653 if (!WriteConsoleOutputW(g_hConOut, // output handle 2654 cb->Buffer, // our buffer 2655 cb->BufferSize, // dimensions of our buffer 2656 BufferCoord, // offset in our buffer 2657 &WriteRegion)) // region to restore 2658 return FALSE; 2659 } 2660 } 2661 2662 return TRUE; 2663 } 2664 2665 # define FEAT_RESTORE_ORIG_SCREEN 2666 # ifdef FEAT_RESTORE_ORIG_SCREEN 2667 static ConsoleBuffer g_cbOrig = { 0 }; 2668 # endif 2669 static ConsoleBuffer g_cbNonTermcap = { 0 }; 2670 static ConsoleBuffer g_cbTermcap = { 0 }; 2671 2672 # ifdef FEAT_TITLE 2673 char g_szOrigTitle[256] = { 0 }; 2674 HWND g_hWnd = NULL; // also used in os_mswin.c 2675 static HICON g_hOrigIconSmall = NULL; 2676 static HICON g_hOrigIcon = NULL; 2677 static HICON g_hVimIcon = NULL; 2678 static BOOL g_fCanChangeIcon = FALSE; 2679 2680 // ICON* are not defined in VC++ 4.0 2681 # ifndef ICON_SMALL 2682 # define ICON_SMALL 0 2683 # endif 2684 # ifndef ICON_BIG 2685 # define ICON_BIG 1 2686 # endif 2687 /* 2688 * GetConsoleIcon() 2689 * Description: 2690 * Attempts to retrieve the small icon and/or the big icon currently in 2691 * use by a given window. 2692 * Returns: 2693 * TRUE on success 2694 */ 2695 static BOOL 2696 GetConsoleIcon( 2697 HWND hWnd, 2698 HICON *phIconSmall, 2699 HICON *phIcon) 2700 { 2701 if (hWnd == NULL) 2702 return FALSE; 2703 2704 if (phIconSmall != NULL) 2705 *phIconSmall = (HICON)SendMessage(hWnd, WM_GETICON, 2706 (WPARAM)ICON_SMALL, (LPARAM)0); 2707 if (phIcon != NULL) 2708 *phIcon = (HICON)SendMessage(hWnd, WM_GETICON, 2709 (WPARAM)ICON_BIG, (LPARAM)0); 2710 return TRUE; 2711 } 2712 2713 /* 2714 * SetConsoleIcon() 2715 * Description: 2716 * Attempts to change the small icon and/or the big icon currently in 2717 * use by a given window. 2718 * Returns: 2719 * TRUE on success 2720 */ 2721 static BOOL 2722 SetConsoleIcon( 2723 HWND hWnd, 2724 HICON hIconSmall, 2725 HICON hIcon) 2726 { 2727 if (hWnd == NULL) 2728 return FALSE; 2729 2730 if (hIconSmall != NULL) 2731 SendMessage(hWnd, WM_SETICON, 2732 (WPARAM)ICON_SMALL, (LPARAM)hIconSmall); 2733 if (hIcon != NULL) 2734 SendMessage(hWnd, WM_SETICON, 2735 (WPARAM)ICON_BIG, (LPARAM) hIcon); 2736 return TRUE; 2737 } 2738 2739 /* 2740 * SaveConsoleTitleAndIcon() 2741 * Description: 2742 * Saves the current console window title in g_szOrigTitle, for later 2743 * restoration. Also, attempts to obtain a handle to the console window, 2744 * and use it to save the small and big icons currently in use by the 2745 * console window. This is not always possible on some versions of Windows; 2746 * nor is it possible when running Vim remotely using Telnet (since the 2747 * console window the user sees is owned by a remote process). 2748 */ 2749 static void 2750 SaveConsoleTitleAndIcon(void) 2751 { 2752 // Save the original title. 2753 if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle))) 2754 return; 2755 2756 /* 2757 * Obtain a handle to the console window using GetConsoleWindow() from 2758 * KERNEL32.DLL; we need to handle in order to change the window icon. 2759 * This function only exists on NT-based Windows, starting with Windows 2760 * 2000. On older operating systems, we can't change the window icon 2761 * anyway. 2762 */ 2763 g_hWnd = GetConsoleWindow(); 2764 if (g_hWnd == NULL) 2765 return; 2766 2767 // Save the original console window icon. 2768 GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon); 2769 if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL) 2770 return; 2771 2772 // Extract the first icon contained in the Vim executable. 2773 if (mch_icon_load((HANDLE *)&g_hVimIcon) == FAIL || g_hVimIcon == NULL) 2774 g_hVimIcon = ExtractIcon(NULL, (LPCSTR)exe_name, 0); 2775 if (g_hVimIcon != NULL) 2776 g_fCanChangeIcon = TRUE; 2777 } 2778 # endif 2779 2780 static int g_fWindInitCalled = FALSE; 2781 static int g_fTermcapMode = FALSE; 2782 static CONSOLE_CURSOR_INFO g_cci; 2783 static DWORD g_cmodein = 0; 2784 static DWORD g_cmodeout = 0; 2785 2786 /* 2787 * non-GUI version of mch_init(). 2788 */ 2789 static void 2790 mch_init_c(void) 2791 { 2792 # ifndef FEAT_RESTORE_ORIG_SCREEN 2793 CONSOLE_SCREEN_BUFFER_INFO csbi; 2794 # endif 2795 # ifndef __MINGW32__ 2796 extern int _fmode; 2797 # endif 2798 2799 // Silently handle invalid parameters to CRT functions 2800 SET_INVALID_PARAM_HANDLER; 2801 2802 // Let critical errors result in a failure, not in a dialog box. Required 2803 // for the timestamp test to work on removed floppies. 2804 SetErrorMode(SEM_FAILCRITICALERRORS); 2805 2806 _fmode = O_BINARY; // we do our own CR-LF translation 2807 out_flush(); 2808 2809 // Obtain handles for the standard Console I/O devices 2810 if (read_cmd_fd == 0) 2811 g_hConIn = GetStdHandle(STD_INPUT_HANDLE); 2812 else 2813 create_conin(); 2814 g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE); 2815 2816 # ifdef FEAT_RESTORE_ORIG_SCREEN 2817 // Save the initial console buffer for later restoration 2818 SaveConsoleBuffer(&g_cbOrig); 2819 g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes; 2820 # else 2821 // Get current text attributes 2822 GetConsoleScreenBufferInfo(g_hConOut, &csbi); 2823 g_attrCurrent = g_attrDefault = csbi.wAttributes; 2824 # endif 2825 if (cterm_normal_fg_color == 0) 2826 cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1; 2827 if (cterm_normal_bg_color == 0) 2828 cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1; 2829 2830 // Fg and Bg color index number at startup 2831 g_color_index_fg = g_attrDefault & 0xf; 2832 g_color_index_bg = (g_attrDefault >> 4) & 0xf; 2833 2834 // set termcap codes to current text attributes 2835 update_tcap(g_attrCurrent); 2836 2837 GetConsoleCursorInfo(g_hConOut, &g_cci); 2838 GetConsoleMode(g_hConIn, &g_cmodein); 2839 GetConsoleMode(g_hConOut, &g_cmodeout); 2840 2841 # ifdef FEAT_TITLE 2842 SaveConsoleTitleAndIcon(); 2843 /* 2844 * Set both the small and big icons of the console window to Vim's icon. 2845 * Note that Vim presently only has one size of icon (32x32), but it 2846 * automatically gets scaled down to 16x16 when setting the small icon. 2847 */ 2848 if (g_fCanChangeIcon) 2849 SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon); 2850 # endif 2851 2852 ui_get_shellsize(); 2853 2854 # ifdef MCH_WRITE_DUMP 2855 fdDump = fopen("dump", "wt"); 2856 2857 if (fdDump) 2858 { 2859 time_t t; 2860 2861 time(&t); 2862 fputs(ctime(&t), fdDump); 2863 fflush(fdDump); 2864 } 2865 # endif 2866 2867 g_fWindInitCalled = TRUE; 2868 2869 g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); 2870 2871 # ifdef FEAT_CLIPBOARD 2872 win_clip_init(); 2873 # endif 2874 2875 vtp_flag_init(); 2876 vtp_init(); 2877 wt_init(); 2878 } 2879 2880 /* 2881 * non-GUI version of mch_exit(). 2882 * Shut down and exit with status `r' 2883 * Careful: mch_exit() may be called before mch_init()! 2884 */ 2885 static void 2886 mch_exit_c(int r) 2887 { 2888 exiting = TRUE; 2889 2890 vtp_exit(); 2891 2892 stoptermcap(); 2893 if (g_fWindInitCalled) 2894 settmode(TMODE_COOK); 2895 2896 ml_close_all(TRUE); // remove all memfiles 2897 2898 if (g_fWindInitCalled) 2899 { 2900 # ifdef FEAT_TITLE 2901 mch_restore_title(SAVE_RESTORE_BOTH); 2902 /* 2903 * Restore both the small and big icons of the console window to 2904 * what they were at startup. Don't do this when the window is 2905 * closed, Vim would hang here. 2906 */ 2907 if (g_fCanChangeIcon && !g_fForceExit) 2908 SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon); 2909 # endif 2910 2911 # ifdef MCH_WRITE_DUMP 2912 if (fdDump) 2913 { 2914 time_t t; 2915 2916 time(&t); 2917 fputs(ctime(&t), fdDump); 2918 fclose(fdDump); 2919 } 2920 fdDump = NULL; 2921 # endif 2922 } 2923 2924 SetConsoleCursorInfo(g_hConOut, &g_cci); 2925 SetConsoleMode(g_hConIn, g_cmodein); 2926 SetConsoleMode(g_hConOut, g_cmodeout); 2927 2928 # ifdef DYNAMIC_GETTEXT 2929 dyn_libintl_end(); 2930 # endif 2931 2932 exit(r); 2933 } 2934 #endif // !FEAT_GUI_MSWIN 2935 2936 void 2937 mch_init(void) 2938 { 2939 #ifdef VIMDLL 2940 if (gui.starting) 2941 mch_init_g(); 2942 else 2943 mch_init_c(); 2944 #elif defined(FEAT_GUI_MSWIN) 2945 mch_init_g(); 2946 #else 2947 mch_init_c(); 2948 #endif 2949 } 2950 2951 void 2952 mch_exit(int r) 2953 { 2954 #ifdef FEAT_NETBEANS_INTG 2955 netbeans_send_disconnect(); 2956 #endif 2957 2958 #ifdef VIMDLL 2959 if (gui.in_use || gui.starting) 2960 mch_exit_g(r); 2961 else 2962 mch_exit_c(r); 2963 #elif defined(FEAT_GUI_MSWIN) 2964 mch_exit_g(r); 2965 #else 2966 mch_exit_c(r); 2967 #endif 2968 } 2969 2970 /* 2971 * Do we have an interactive window? 2972 */ 2973 int 2974 mch_check_win( 2975 int argc UNUSED, 2976 char **argv UNUSED) 2977 { 2978 get_exe_name(); 2979 2980 #if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL) 2981 return OK; // GUI always has a tty 2982 #else 2983 # ifdef VIMDLL 2984 if (gui.in_use) 2985 return OK; 2986 # endif 2987 if (isatty(1)) 2988 return OK; 2989 return FAIL; 2990 #endif 2991 } 2992 2993 /* 2994 * Set the case of the file name, if it already exists. 2995 * When "len" is > 0, also expand short to long filenames. 2996 */ 2997 void 2998 fname_case( 2999 char_u *name, 3000 int len) 3001 { 3002 int flen; 3003 WCHAR *p; 3004 WCHAR buf[_MAX_PATH + 1]; 3005 3006 flen = (int)STRLEN(name); 3007 if (flen == 0) 3008 return; 3009 3010 slash_adjust(name); 3011 3012 p = enc_to_utf16(name, NULL); 3013 if (p == NULL) 3014 return; 3015 3016 if (GetLongPathNameW(p, buf, _MAX_PATH)) 3017 { 3018 char_u *q = utf16_to_enc(buf, NULL); 3019 3020 if (q != NULL) 3021 { 3022 if (len > 0 || flen >= (int)STRLEN(q)) 3023 vim_strncpy(name, q, (len > 0) ? len - 1 : flen); 3024 vim_free(q); 3025 } 3026 } 3027 vim_free(p); 3028 } 3029 3030 3031 /* 3032 * Insert user name in s[len]. 3033 */ 3034 int 3035 mch_get_user_name( 3036 char_u *s, 3037 int len) 3038 { 3039 WCHAR wszUserName[256 + 1]; // UNLEN is 256 3040 DWORD wcch = sizeof(wszUserName) / sizeof(WCHAR); 3041 3042 if (GetUserNameW(wszUserName, &wcch)) 3043 { 3044 char_u *p = utf16_to_enc(wszUserName, NULL); 3045 3046 if (p != NULL) 3047 { 3048 vim_strncpy(s, p, len - 1); 3049 vim_free(p); 3050 return OK; 3051 } 3052 } 3053 s[0] = NUL; 3054 return FAIL; 3055 } 3056 3057 3058 /* 3059 * Insert host name in s[len]. 3060 */ 3061 void 3062 mch_get_host_name( 3063 char_u *s, 3064 int len) 3065 { 3066 WCHAR wszHostName[256 + 1]; 3067 DWORD wcch = sizeof(wszHostName) / sizeof(WCHAR); 3068 3069 if (GetComputerNameW(wszHostName, &wcch)) 3070 { 3071 char_u *p = utf16_to_enc(wszHostName, NULL); 3072 3073 if (p != NULL) 3074 { 3075 vim_strncpy(s, p, len - 1); 3076 vim_free(p); 3077 return; 3078 } 3079 } 3080 } 3081 3082 3083 /* 3084 * return process ID 3085 */ 3086 long 3087 mch_get_pid(void) 3088 { 3089 return (long)GetCurrentProcessId(); 3090 } 3091 3092 /* 3093 * return TRUE if process "pid" is still running 3094 */ 3095 int 3096 mch_process_running(long pid) 3097 { 3098 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, 0, (DWORD)pid); 3099 DWORD status = 0; 3100 int ret = FALSE; 3101 3102 if (hProcess == NULL) 3103 return FALSE; // might not have access 3104 if (GetExitCodeProcess(hProcess, &status) ) 3105 ret = status == STILL_ACTIVE; 3106 CloseHandle(hProcess); 3107 return ret; 3108 } 3109 3110 /* 3111 * Get name of current directory into buffer 'buf' of length 'len' bytes. 3112 * Return OK for success, FAIL for failure. 3113 */ 3114 int 3115 mch_dirname( 3116 char_u *buf, 3117 int len) 3118 { 3119 WCHAR wbuf[_MAX_PATH + 1]; 3120 3121 /* 3122 * Originally this was: 3123 * return (getcwd(buf, len) != NULL ? OK : FAIL); 3124 * But the Win32s known bug list says that getcwd() doesn't work 3125 * so use the Win32 system call instead. <Negri> 3126 */ 3127 if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0) 3128 { 3129 WCHAR wcbuf[_MAX_PATH + 1]; 3130 char_u *p = NULL; 3131 3132 if (GetLongPathNameW(wbuf, wcbuf, _MAX_PATH) != 0) 3133 { 3134 p = utf16_to_enc(wcbuf, NULL); 3135 if (STRLEN(p) >= (size_t)len) 3136 { 3137 // long path name is too long, fall back to short one 3138 vim_free(p); 3139 p = NULL; 3140 } 3141 } 3142 if (p == NULL) 3143 p = utf16_to_enc(wbuf, NULL); 3144 3145 if (p != NULL) 3146 { 3147 vim_strncpy(buf, p, len - 1); 3148 vim_free(p); 3149 return OK; 3150 } 3151 } 3152 return FAIL; 3153 } 3154 3155 /* 3156 * Get file permissions for "name". 3157 * Return mode_t or -1 for error. 3158 */ 3159 long 3160 mch_getperm(char_u *name) 3161 { 3162 stat_T st; 3163 int n; 3164 3165 n = mch_stat((char *)name, &st); 3166 return n == 0 ? (long)(unsigned short)st.st_mode : -1L; 3167 } 3168 3169 3170 /* 3171 * Set file permission for "name" to "perm". 3172 * 3173 * Return FAIL for failure, OK otherwise. 3174 */ 3175 int 3176 mch_setperm(char_u *name, long perm) 3177 { 3178 long n; 3179 WCHAR *p; 3180 3181 p = enc_to_utf16(name, NULL); 3182 if (p == NULL) 3183 return FAIL; 3184 3185 n = _wchmod(p, perm); 3186 vim_free(p); 3187 if (n == -1) 3188 return FAIL; 3189 3190 win32_set_archive(name); 3191 3192 return OK; 3193 } 3194 3195 /* 3196 * Set hidden flag for "name". 3197 */ 3198 void 3199 mch_hide(char_u *name) 3200 { 3201 int attrs = win32_getattrs(name); 3202 if (attrs == -1) 3203 return; 3204 3205 attrs |= FILE_ATTRIBUTE_HIDDEN; 3206 win32_setattrs(name, attrs); 3207 } 3208 3209 /* 3210 * Return TRUE if file "name" exists and is hidden. 3211 */ 3212 int 3213 mch_ishidden(char_u *name) 3214 { 3215 int f = win32_getattrs(name); 3216 3217 if (f == -1) 3218 return FALSE; // file does not exist at all 3219 3220 return (f & FILE_ATTRIBUTE_HIDDEN) != 0; 3221 } 3222 3223 /* 3224 * return TRUE if "name" is a directory 3225 * return FALSE if "name" is not a directory or upon error 3226 */ 3227 int 3228 mch_isdir(char_u *name) 3229 { 3230 int f = win32_getattrs(name); 3231 3232 if (f == -1) 3233 return FALSE; // file does not exist at all 3234 3235 return (f & FILE_ATTRIBUTE_DIRECTORY) != 0; 3236 } 3237 3238 /* 3239 * return TRUE if "name" is a directory, NOT a symlink to a directory 3240 * return FALSE if "name" is not a directory 3241 * return FALSE for error 3242 */ 3243 int 3244 mch_isrealdir(char_u *name) 3245 { 3246 return mch_isdir(name) && !mch_is_symbolic_link(name); 3247 } 3248 3249 /* 3250 * Create directory "name". 3251 * Return 0 on success, -1 on error. 3252 */ 3253 int 3254 mch_mkdir(char_u *name) 3255 { 3256 WCHAR *p; 3257 int retval; 3258 3259 p = enc_to_utf16(name, NULL); 3260 if (p == NULL) 3261 return -1; 3262 retval = _wmkdir(p); 3263 vim_free(p); 3264 return retval; 3265 } 3266 3267 /* 3268 * Delete directory "name". 3269 * Return 0 on success, -1 on error. 3270 */ 3271 int 3272 mch_rmdir(char_u *name) 3273 { 3274 WCHAR *p; 3275 int retval; 3276 3277 p = enc_to_utf16(name, NULL); 3278 if (p == NULL) 3279 return -1; 3280 retval = _wrmdir(p); 3281 vim_free(p); 3282 return retval; 3283 } 3284 3285 /* 3286 * Return TRUE if file "fname" has more than one link. 3287 */ 3288 int 3289 mch_is_hard_link(char_u *fname) 3290 { 3291 BY_HANDLE_FILE_INFORMATION info; 3292 3293 return win32_fileinfo(fname, &info) == FILEINFO_OK 3294 && info.nNumberOfLinks > 1; 3295 } 3296 3297 /* 3298 * Return TRUE if "name" is a symbolic link (or a junction). 3299 */ 3300 int 3301 mch_is_symbolic_link(char_u *name) 3302 { 3303 HANDLE hFind; 3304 int res = FALSE; 3305 DWORD fileFlags = 0, reparseTag = 0; 3306 WCHAR *wn; 3307 WIN32_FIND_DATAW findDataW; 3308 3309 wn = enc_to_utf16(name, NULL); 3310 if (wn == NULL) 3311 return FALSE; 3312 3313 hFind = FindFirstFileW(wn, &findDataW); 3314 vim_free(wn); 3315 if (hFind != INVALID_HANDLE_VALUE) 3316 { 3317 fileFlags = findDataW.dwFileAttributes; 3318 reparseTag = findDataW.dwReserved0; 3319 FindClose(hFind); 3320 } 3321 3322 if ((fileFlags & FILE_ATTRIBUTE_REPARSE_POINT) 3323 && (reparseTag == IO_REPARSE_TAG_SYMLINK 3324 || reparseTag == IO_REPARSE_TAG_MOUNT_POINT)) 3325 res = TRUE; 3326 3327 return res; 3328 } 3329 3330 /* 3331 * Return TRUE if file "fname" has more than one link or if it is a symbolic 3332 * link. 3333 */ 3334 int 3335 mch_is_linked(char_u *fname) 3336 { 3337 if (mch_is_hard_link(fname) || mch_is_symbolic_link(fname)) 3338 return TRUE; 3339 return FALSE; 3340 } 3341 3342 /* 3343 * Get the by-handle-file-information for "fname". 3344 * Returns FILEINFO_OK when OK. 3345 * Returns FILEINFO_ENC_FAIL when enc_to_utf16() failed. 3346 * Returns FILEINFO_READ_FAIL when CreateFile() failed. 3347 * Returns FILEINFO_INFO_FAIL when GetFileInformationByHandle() failed. 3348 */ 3349 int 3350 win32_fileinfo(char_u *fname, BY_HANDLE_FILE_INFORMATION *info) 3351 { 3352 HANDLE hFile; 3353 int res = FILEINFO_READ_FAIL; 3354 WCHAR *wn; 3355 3356 wn = enc_to_utf16(fname, NULL); 3357 if (wn == NULL) 3358 return FILEINFO_ENC_FAIL; 3359 3360 hFile = CreateFileW(wn, // file name 3361 GENERIC_READ, // access mode 3362 FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode 3363 NULL, // security descriptor 3364 OPEN_EXISTING, // creation disposition 3365 FILE_FLAG_BACKUP_SEMANTICS, // file attributes 3366 NULL); // handle to template file 3367 vim_free(wn); 3368 3369 if (hFile != INVALID_HANDLE_VALUE) 3370 { 3371 if (GetFileInformationByHandle(hFile, info) != 0) 3372 res = FILEINFO_OK; 3373 else 3374 res = FILEINFO_INFO_FAIL; 3375 CloseHandle(hFile); 3376 } 3377 3378 return res; 3379 } 3380 3381 /* 3382 * get file attributes for `name' 3383 * -1 : error 3384 * else FILE_ATTRIBUTE_* defined in winnt.h 3385 */ 3386 static int 3387 win32_getattrs(char_u *name) 3388 { 3389 int attr; 3390 WCHAR *p; 3391 3392 p = enc_to_utf16(name, NULL); 3393 if (p == NULL) 3394 return INVALID_FILE_ATTRIBUTES; 3395 3396 attr = GetFileAttributesW(p); 3397 vim_free(p); 3398 3399 return attr; 3400 } 3401 3402 /* 3403 * set file attributes for `name' to `attrs' 3404 * 3405 * return -1 for failure, 0 otherwise 3406 */ 3407 static int 3408 win32_setattrs(char_u *name, int attrs) 3409 { 3410 int res; 3411 WCHAR *p; 3412 3413 p = enc_to_utf16(name, NULL); 3414 if (p == NULL) 3415 return -1; 3416 3417 res = SetFileAttributesW(p, attrs); 3418 vim_free(p); 3419 3420 return res ? 0 : -1; 3421 } 3422 3423 /* 3424 * Set archive flag for "name". 3425 */ 3426 static int 3427 win32_set_archive(char_u *name) 3428 { 3429 int attrs = win32_getattrs(name); 3430 if (attrs == -1) 3431 return -1; 3432 3433 attrs |= FILE_ATTRIBUTE_ARCHIVE; 3434 return win32_setattrs(name, attrs); 3435 } 3436 3437 /* 3438 * Return TRUE if file or directory "name" is writable (not readonly). 3439 * Strange semantics of Win32: a readonly directory is writable, but you can't 3440 * delete a file. Let's say this means it is writable. 3441 */ 3442 int 3443 mch_writable(char_u *name) 3444 { 3445 int attrs = win32_getattrs(name); 3446 3447 return (attrs != -1 && (!(attrs & FILE_ATTRIBUTE_READONLY) 3448 || (attrs & FILE_ATTRIBUTE_DIRECTORY))); 3449 } 3450 3451 /* 3452 * Return TRUE if "name" can be executed, FALSE if not. 3453 * If "use_path" is FALSE only check if "name" is executable. 3454 * When returning TRUE and "path" is not NULL save the path and set "*path" to 3455 * the allocated memory. 3456 */ 3457 int 3458 mch_can_exe(char_u *name, char_u **path, int use_path) 3459 { 3460 return executable_exists((char *)name, path, TRUE, TRUE); 3461 } 3462 3463 /* 3464 * Check what "name" is: 3465 * NODE_NORMAL: file or directory (or doesn't exist) 3466 * NODE_WRITABLE: writable device, socket, fifo, etc. 3467 * NODE_OTHER: non-writable things 3468 */ 3469 int 3470 mch_nodetype(char_u *name) 3471 { 3472 HANDLE hFile; 3473 int type; 3474 WCHAR *wn; 3475 3476 // We can't open a file with a name "\\.\con" or "\\.\prn" and trying to 3477 // read from it later will cause Vim to hang. Thus return NODE_WRITABLE 3478 // here. 3479 if (STRNCMP(name, "\\\\.\\", 4) == 0) 3480 return NODE_WRITABLE; 3481 3482 wn = enc_to_utf16(name, NULL); 3483 if (wn == NULL) 3484 return NODE_NORMAL; 3485 3486 hFile = CreateFileW(wn, // file name 3487 GENERIC_WRITE, // access mode 3488 0, // share mode 3489 NULL, // security descriptor 3490 OPEN_EXISTING, // creation disposition 3491 0, // file attributes 3492 NULL); // handle to template file 3493 vim_free(wn); 3494 if (hFile == INVALID_HANDLE_VALUE) 3495 return NODE_NORMAL; 3496 3497 type = GetFileType(hFile); 3498 CloseHandle(hFile); 3499 if (type == FILE_TYPE_CHAR) 3500 return NODE_WRITABLE; 3501 if (type == FILE_TYPE_DISK) 3502 return NODE_NORMAL; 3503 return NODE_OTHER; 3504 } 3505 3506 #ifdef HAVE_ACL 3507 struct my_acl 3508 { 3509 PSECURITY_DESCRIPTOR pSecurityDescriptor; 3510 PSID pSidOwner; 3511 PSID pSidGroup; 3512 PACL pDacl; 3513 PACL pSacl; 3514 }; 3515 #endif 3516 3517 /* 3518 * Return a pointer to the ACL of file "fname" in allocated memory. 3519 * Return NULL if the ACL is not available for whatever reason. 3520 */ 3521 vim_acl_T 3522 mch_get_acl(char_u *fname) 3523 { 3524 #ifndef HAVE_ACL 3525 return (vim_acl_T)NULL; 3526 #else 3527 struct my_acl *p = NULL; 3528 DWORD err; 3529 3530 p = ALLOC_CLEAR_ONE(struct my_acl); 3531 if (p != NULL) 3532 { 3533 WCHAR *wn; 3534 3535 wn = enc_to_utf16(fname, NULL); 3536 if (wn == NULL) 3537 { 3538 vim_free(p); 3539 return NULL; 3540 } 3541 3542 // Try to retrieve the entire security descriptor. 3543 err = GetNamedSecurityInfoW( 3544 wn, // Abstract filename 3545 SE_FILE_OBJECT, // File Object 3546 OWNER_SECURITY_INFORMATION | 3547 GROUP_SECURITY_INFORMATION | 3548 DACL_SECURITY_INFORMATION | 3549 SACL_SECURITY_INFORMATION, 3550 &p->pSidOwner, // Ownership information. 3551 &p->pSidGroup, // Group membership. 3552 &p->pDacl, // Discretionary information. 3553 &p->pSacl, // For auditing purposes. 3554 &p->pSecurityDescriptor); 3555 if (err == ERROR_ACCESS_DENIED || 3556 err == ERROR_PRIVILEGE_NOT_HELD) 3557 { 3558 // Retrieve only DACL. 3559 (void)GetNamedSecurityInfoW( 3560 wn, 3561 SE_FILE_OBJECT, 3562 DACL_SECURITY_INFORMATION, 3563 NULL, 3564 NULL, 3565 &p->pDacl, 3566 NULL, 3567 &p->pSecurityDescriptor); 3568 } 3569 if (p->pSecurityDescriptor == NULL) 3570 { 3571 mch_free_acl((vim_acl_T)p); 3572 p = NULL; 3573 } 3574 vim_free(wn); 3575 } 3576 3577 return (vim_acl_T)p; 3578 #endif 3579 } 3580 3581 #ifdef HAVE_ACL 3582 /* 3583 * Check if "acl" contains inherited ACE. 3584 */ 3585 static BOOL 3586 is_acl_inherited(PACL acl) 3587 { 3588 DWORD i; 3589 ACL_SIZE_INFORMATION acl_info; 3590 PACCESS_ALLOWED_ACE ace; 3591 3592 acl_info.AceCount = 0; 3593 GetAclInformation(acl, &acl_info, sizeof(acl_info), AclSizeInformation); 3594 for (i = 0; i < acl_info.AceCount; i++) 3595 { 3596 GetAce(acl, i, (LPVOID *)&ace); 3597 if (ace->Header.AceFlags & INHERITED_ACE) 3598 return TRUE; 3599 } 3600 return FALSE; 3601 } 3602 #endif 3603 3604 /* 3605 * Set the ACL of file "fname" to "acl" (unless it's NULL). 3606 * Errors are ignored. 3607 * This must only be called with "acl" equal to what mch_get_acl() returned. 3608 */ 3609 void 3610 mch_set_acl(char_u *fname, vim_acl_T acl) 3611 { 3612 #ifdef HAVE_ACL 3613 struct my_acl *p = (struct my_acl *)acl; 3614 SECURITY_INFORMATION sec_info = 0; 3615 WCHAR *wn; 3616 3617 if (p == NULL) 3618 return; 3619 3620 wn = enc_to_utf16(fname, NULL); 3621 if (wn == NULL) 3622 return; 3623 3624 // Set security flags 3625 if (p->pSidOwner) 3626 sec_info |= OWNER_SECURITY_INFORMATION; 3627 if (p->pSidGroup) 3628 sec_info |= GROUP_SECURITY_INFORMATION; 3629 if (p->pDacl) 3630 { 3631 sec_info |= DACL_SECURITY_INFORMATION; 3632 // Do not inherit its parent's DACL. 3633 // If the DACL is inherited, Cygwin permissions would be changed. 3634 if (!is_acl_inherited(p->pDacl)) 3635 sec_info |= PROTECTED_DACL_SECURITY_INFORMATION; 3636 } 3637 if (p->pSacl) 3638 sec_info |= SACL_SECURITY_INFORMATION; 3639 3640 (void)SetNamedSecurityInfoW( 3641 wn, // Abstract filename 3642 SE_FILE_OBJECT, // File Object 3643 sec_info, 3644 p->pSidOwner, // Ownership information. 3645 p->pSidGroup, // Group membership. 3646 p->pDacl, // Discretionary information. 3647 p->pSacl // For auditing purposes. 3648 ); 3649 vim_free(wn); 3650 #endif 3651 } 3652 3653 void 3654 mch_free_acl(vim_acl_T acl) 3655 { 3656 #ifdef HAVE_ACL 3657 struct my_acl *p = (struct my_acl *)acl; 3658 3659 if (p != NULL) 3660 { 3661 LocalFree(p->pSecurityDescriptor); // Free the memory just in case 3662 vim_free(p); 3663 } 3664 #endif 3665 } 3666 3667 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 3668 3669 /* 3670 * handler for ctrl-break, ctrl-c interrupts, and fatal events. 3671 */ 3672 static BOOL WINAPI 3673 handler_routine( 3674 DWORD dwCtrlType) 3675 { 3676 INPUT_RECORD ir; 3677 DWORD out; 3678 3679 switch (dwCtrlType) 3680 { 3681 case CTRL_C_EVENT: 3682 if (ctrl_c_interrupts) 3683 g_fCtrlCPressed = TRUE; 3684 return TRUE; 3685 3686 case CTRL_BREAK_EVENT: 3687 g_fCBrkPressed = TRUE; 3688 ctrl_break_was_pressed = TRUE; 3689 // ReadConsoleInput is blocking, send a key event to continue. 3690 ir.EventType = KEY_EVENT; 3691 ir.Event.KeyEvent.bKeyDown = TRUE; 3692 ir.Event.KeyEvent.wRepeatCount = 1; 3693 ir.Event.KeyEvent.wVirtualKeyCode = VK_CANCEL; 3694 ir.Event.KeyEvent.wVirtualScanCode = 0; 3695 ir.Event.KeyEvent.dwControlKeyState = 0; 3696 ir.Event.KeyEvent.uChar.UnicodeChar = 0; 3697 WriteConsoleInput(g_hConIn, &ir, 1, &out); 3698 return TRUE; 3699 3700 // fatal events: shut down gracefully 3701 case CTRL_CLOSE_EVENT: 3702 case CTRL_LOGOFF_EVENT: 3703 case CTRL_SHUTDOWN_EVENT: 3704 windgoto((int)Rows - 1, 0); 3705 g_fForceExit = TRUE; 3706 3707 vim_snprintf((char *)IObuff, IOSIZE, _("Vim: Caught %s event\n"), 3708 (dwCtrlType == CTRL_CLOSE_EVENT 3709 ? _("close") 3710 : dwCtrlType == CTRL_LOGOFF_EVENT 3711 ? _("logoff") 3712 : _("shutdown"))); 3713 # ifdef DEBUG 3714 OutputDebugString(IObuff); 3715 # endif 3716 3717 preserve_exit(); // output IObuff, preserve files and exit 3718 3719 return TRUE; // not reached 3720 3721 default: 3722 return FALSE; 3723 } 3724 } 3725 3726 3727 /* 3728 * set the tty in (raw) ? "raw" : "cooked" mode 3729 */ 3730 void 3731 mch_settmode(tmode_T tmode) 3732 { 3733 DWORD cmodein; 3734 DWORD cmodeout; 3735 BOOL bEnableHandler; 3736 3737 # ifdef VIMDLL 3738 if (gui.in_use) 3739 return; 3740 # endif 3741 GetConsoleMode(g_hConIn, &cmodein); 3742 GetConsoleMode(g_hConOut, &cmodeout); 3743 if (tmode == TMODE_RAW) 3744 { 3745 cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | 3746 ENABLE_ECHO_INPUT); 3747 if (g_fMouseActive) 3748 cmodein |= ENABLE_MOUSE_INPUT; 3749 cmodeout &= ~( 3750 # ifdef FEAT_TERMGUICOLORS 3751 // Do not turn off the ENABLE_PROCESSED_OUTPUT flag when using 3752 // VTP. 3753 ((vtp_working) ? 0 : ENABLE_PROCESSED_OUTPUT) | 3754 # else 3755 ENABLE_PROCESSED_OUTPUT | 3756 # endif 3757 ENABLE_WRAP_AT_EOL_OUTPUT); 3758 bEnableHandler = TRUE; 3759 } 3760 else // cooked 3761 { 3762 cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | 3763 ENABLE_ECHO_INPUT); 3764 cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT); 3765 bEnableHandler = FALSE; 3766 } 3767 SetConsoleMode(g_hConIn, cmodein); 3768 SetConsoleMode(g_hConOut, cmodeout); 3769 SetConsoleCtrlHandler(handler_routine, bEnableHandler); 3770 3771 # ifdef MCH_WRITE_DUMP 3772 if (fdDump) 3773 { 3774 fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n", 3775 tmode == TMODE_RAW ? "raw" : 3776 tmode == TMODE_COOK ? "cooked" : "normal", 3777 cmodein, cmodeout); 3778 fflush(fdDump); 3779 } 3780 # endif 3781 } 3782 3783 3784 /* 3785 * Get the size of the current window in `Rows' and `Columns' 3786 * Return OK when size could be determined, FAIL otherwise. 3787 */ 3788 int 3789 mch_get_shellsize(void) 3790 { 3791 CONSOLE_SCREEN_BUFFER_INFO csbi; 3792 3793 # ifdef VIMDLL 3794 if (gui.in_use) 3795 return OK; 3796 # endif 3797 if (!g_fTermcapMode && g_cbTermcap.IsValid) 3798 { 3799 /* 3800 * For some reason, we are trying to get the screen dimensions 3801 * even though we are not in termcap mode. The 'Rows' and 'Columns' 3802 * variables are really intended to mean the size of Vim screen 3803 * while in termcap mode. 3804 */ 3805 Rows = g_cbTermcap.Info.dwSize.Y; 3806 Columns = g_cbTermcap.Info.dwSize.X; 3807 } 3808 else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi)) 3809 { 3810 Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; 3811 Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; 3812 } 3813 else 3814 { 3815 Rows = 25; 3816 Columns = 80; 3817 } 3818 return OK; 3819 } 3820 3821 /* 3822 * Resize console buffer to 'COORD' 3823 */ 3824 static void 3825 ResizeConBuf( 3826 HANDLE hConsole, 3827 COORD coordScreen) 3828 { 3829 if (!SetConsoleScreenBufferSize(hConsole, coordScreen)) 3830 { 3831 # ifdef MCH_WRITE_DUMP 3832 if (fdDump) 3833 { 3834 fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n", 3835 GetLastError()); 3836 fflush(fdDump); 3837 } 3838 # endif 3839 } 3840 } 3841 3842 /* 3843 * Resize console window size to 'srWindowRect' 3844 */ 3845 static void 3846 ResizeWindow( 3847 HANDLE hConsole, 3848 SMALL_RECT srWindowRect) 3849 { 3850 if (!SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect)) 3851 { 3852 # ifdef MCH_WRITE_DUMP 3853 if (fdDump) 3854 { 3855 fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n", 3856 GetLastError()); 3857 fflush(fdDump); 3858 } 3859 # endif 3860 } 3861 } 3862 3863 /* 3864 * Set a console window to `xSize' * `ySize' 3865 */ 3866 static void 3867 ResizeConBufAndWindow( 3868 HANDLE hConsole, 3869 int xSize, 3870 int ySize) 3871 { 3872 CONSOLE_SCREEN_BUFFER_INFO csbi; // hold current console buffer info 3873 SMALL_RECT srWindowRect; // hold the new console size 3874 COORD coordScreen; 3875 COORD cursor; 3876 static int resized = FALSE; 3877 3878 # ifdef MCH_WRITE_DUMP 3879 if (fdDump) 3880 { 3881 fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize); 3882 fflush(fdDump); 3883 } 3884 # endif 3885 3886 // get the largest size we can size the console window to 3887 coordScreen = GetLargestConsoleWindowSize(hConsole); 3888 3889 // define the new console window size and scroll position 3890 srWindowRect.Left = srWindowRect.Top = (SHORT) 0; 3891 srWindowRect.Right = (SHORT) (min(xSize, coordScreen.X) - 1); 3892 srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1); 3893 3894 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi)) 3895 { 3896 int sx, sy; 3897 3898 sx = csbi.srWindow.Right - csbi.srWindow.Left + 1; 3899 sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; 3900 if (sy < ySize || sx < xSize) 3901 { 3902 /* 3903 * Increasing number of lines/columns, do buffer first. 3904 * Use the maximal size in x and y direction. 3905 */ 3906 if (sy < ySize) 3907 coordScreen.Y = ySize; 3908 else 3909 coordScreen.Y = sy; 3910 if (sx < xSize) 3911 coordScreen.X = xSize; 3912 else 3913 coordScreen.X = sx; 3914 SetConsoleScreenBufferSize(hConsole, coordScreen); 3915 } 3916 } 3917 3918 // define the new console buffer size 3919 coordScreen.X = xSize; 3920 coordScreen.Y = ySize; 3921 3922 // In the new console call API, only the first time in reverse order 3923 if (!vtp_working || resized) 3924 { 3925 ResizeWindow(hConsole, srWindowRect); 3926 ResizeConBuf(hConsole, coordScreen); 3927 } 3928 else 3929 { 3930 // Workaround for a Windows 10 bug 3931 cursor.X = srWindowRect.Left; 3932 cursor.Y = srWindowRect.Top; 3933 SetConsoleCursorPosition(hConsole, cursor); 3934 3935 ResizeConBuf(hConsole, coordScreen); 3936 ResizeWindow(hConsole, srWindowRect); 3937 resized = TRUE; 3938 } 3939 } 3940 3941 3942 /* 3943 * Set the console window to `Rows' * `Columns' 3944 */ 3945 void 3946 mch_set_shellsize(void) 3947 { 3948 COORD coordScreen; 3949 3950 # ifdef VIMDLL 3951 if (gui.in_use) 3952 return; 3953 # endif 3954 // Don't change window size while still starting up 3955 if (suppress_winsize != 0) 3956 { 3957 suppress_winsize = 2; 3958 return; 3959 } 3960 3961 if (term_console) 3962 { 3963 coordScreen = GetLargestConsoleWindowSize(g_hConOut); 3964 3965 // Clamp Rows and Columns to reasonable values 3966 if (Rows > coordScreen.Y) 3967 Rows = coordScreen.Y; 3968 if (Columns > coordScreen.X) 3969 Columns = coordScreen.X; 3970 3971 ResizeConBufAndWindow(g_hConOut, Columns, Rows); 3972 } 3973 } 3974 3975 /* 3976 * Rows and/or Columns has changed. 3977 */ 3978 void 3979 mch_new_shellsize(void) 3980 { 3981 # ifdef VIMDLL 3982 if (gui.in_use) 3983 return; 3984 # endif 3985 set_scroll_region(0, 0, Columns - 1, Rows - 1); 3986 } 3987 3988 3989 /* 3990 * Called when started up, to set the winsize that was delayed. 3991 */ 3992 void 3993 mch_set_winsize_now(void) 3994 { 3995 if (suppress_winsize == 2) 3996 { 3997 suppress_winsize = 0; 3998 mch_set_shellsize(); 3999 shell_resized(); 4000 } 4001 suppress_winsize = 0; 4002 } 4003 #endif // FEAT_GUI_MSWIN 4004 4005 static BOOL 4006 vim_create_process( 4007 char *cmd, 4008 BOOL inherit_handles, 4009 DWORD flags, 4010 STARTUPINFO *si, 4011 PROCESS_INFORMATION *pi, 4012 LPVOID *env, 4013 char *cwd) 4014 { 4015 BOOL ret = FALSE; 4016 WCHAR *wcmd, *wcwd = NULL; 4017 4018 wcmd = enc_to_utf16((char_u *)cmd, NULL); 4019 if (wcmd == NULL) 4020 return FALSE; 4021 if (cwd != NULL) 4022 { 4023 wcwd = enc_to_utf16((char_u *)cwd, NULL); 4024 if (wcwd == NULL) 4025 goto theend; 4026 } 4027 4028 ret = CreateProcessW( 4029 NULL, // Executable name 4030 wcmd, // Command to execute 4031 NULL, // Process security attributes 4032 NULL, // Thread security attributes 4033 inherit_handles, // Inherit handles 4034 flags, // Creation flags 4035 env, // Environment 4036 wcwd, // Current directory 4037 (LPSTARTUPINFOW)si, // Startup information 4038 pi); // Process information 4039 theend: 4040 vim_free(wcmd); 4041 vim_free(wcwd); 4042 return ret; 4043 } 4044 4045 4046 static HINSTANCE 4047 vim_shell_execute( 4048 char *cmd, 4049 INT n_show_cmd) 4050 { 4051 HINSTANCE ret; 4052 WCHAR *wcmd; 4053 4054 wcmd = enc_to_utf16((char_u *)cmd, NULL); 4055 if (wcmd == NULL) 4056 return (HINSTANCE) 0; 4057 4058 ret = ShellExecuteW(NULL, NULL, wcmd, NULL, NULL, n_show_cmd); 4059 vim_free(wcmd); 4060 return ret; 4061 } 4062 4063 4064 #if defined(FEAT_GUI_MSWIN) || defined(PROTO) 4065 4066 /* 4067 * Specialised version of system() for Win32 GUI mode. 4068 * This version proceeds as follows: 4069 * 1. Create a console window for use by the subprocess 4070 * 2. Run the subprocess (it gets the allocated console by default) 4071 * 3. Wait for the subprocess to terminate and get its exit code 4072 * 4. Prompt the user to press a key to close the console window 4073 */ 4074 static int 4075 mch_system_classic(char *cmd, int options) 4076 { 4077 STARTUPINFO si; 4078 PROCESS_INFORMATION pi; 4079 DWORD ret = 0; 4080 HWND hwnd = GetFocus(); 4081 4082 si.cb = sizeof(si); 4083 si.lpReserved = NULL; 4084 si.lpDesktop = NULL; 4085 si.lpTitle = NULL; 4086 si.dwFlags = STARTF_USESHOWWINDOW; 4087 /* 4088 * It's nicer to run a filter command in a minimized window. 4089 * Don't activate the window to keep focus on Vim. 4090 */ 4091 if (options & SHELL_DOOUT) 4092 si.wShowWindow = SW_SHOWMINNOACTIVE; 4093 else 4094 si.wShowWindow = SW_SHOWNORMAL; 4095 si.cbReserved2 = 0; 4096 si.lpReserved2 = NULL; 4097 4098 // Now, run the command 4099 vim_create_process(cmd, FALSE, 4100 CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE, 4101 &si, &pi, NULL, NULL); 4102 4103 // Wait for the command to terminate before continuing 4104 { 4105 # ifdef FEAT_GUI 4106 int delay = 1; 4107 4108 // Keep updating the window while waiting for the shell to finish. 4109 for (;;) 4110 { 4111 MSG msg; 4112 4113 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE)) 4114 { 4115 TranslateMessage(&msg); 4116 pDispatchMessage(&msg); 4117 delay = 1; 4118 continue; 4119 } 4120 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT) 4121 break; 4122 4123 // We start waiting for a very short time and then increase it, so 4124 // that we respond quickly when the process is quick, and don't 4125 // consume too much overhead when it's slow. 4126 if (delay < 50) 4127 delay += 10; 4128 } 4129 # else 4130 WaitForSingleObject(pi.hProcess, INFINITE); 4131 # endif 4132 4133 // Get the command exit code 4134 GetExitCodeProcess(pi.hProcess, &ret); 4135 } 4136 4137 // Close the handles to the subprocess, so that it goes away 4138 CloseHandle(pi.hThread); 4139 CloseHandle(pi.hProcess); 4140 4141 // Try to get input focus back. Doesn't always work though. 4142 PostMessage(hwnd, WM_SETFOCUS, 0, 0); 4143 4144 return ret; 4145 } 4146 4147 /* 4148 * Thread launched by the gui to send the current buffer data to the 4149 * process. This way avoid to hang up vim totally if the children 4150 * process take a long time to process the lines. 4151 */ 4152 static unsigned int __stdcall 4153 sub_process_writer(LPVOID param) 4154 { 4155 HANDLE g_hChildStd_IN_Wr = param; 4156 linenr_T lnum = curbuf->b_op_start.lnum; 4157 DWORD len = 0; 4158 DWORD l; 4159 char_u *lp = ml_get(lnum); 4160 char_u *s; 4161 int written = 0; 4162 4163 for (;;) 4164 { 4165 l = (DWORD)STRLEN(lp + written); 4166 if (l == 0) 4167 len = 0; 4168 else if (lp[written] == NL) 4169 { 4170 // NL -> NUL translation 4171 WriteFile(g_hChildStd_IN_Wr, "", 1, &len, NULL); 4172 } 4173 else 4174 { 4175 s = vim_strchr(lp + written, NL); 4176 WriteFile(g_hChildStd_IN_Wr, (char *)lp + written, 4177 s == NULL ? l : (DWORD)(s - (lp + written)), 4178 &len, NULL); 4179 } 4180 if (len == (int)l) 4181 { 4182 // Finished a line, add a NL, unless this line should not have 4183 // one. 4184 if (lnum != curbuf->b_op_end.lnum 4185 || (!curbuf->b_p_bin 4186 && curbuf->b_p_fixeol) 4187 || (lnum != curbuf->b_no_eol_lnum 4188 && (lnum != curbuf->b_ml.ml_line_count 4189 || curbuf->b_p_eol))) 4190 { 4191 WriteFile(g_hChildStd_IN_Wr, "\n", 1, 4192 (LPDWORD)&vim_ignored, NULL); 4193 } 4194 4195 ++lnum; 4196 if (lnum > curbuf->b_op_end.lnum) 4197 break; 4198 4199 lp = ml_get(lnum); 4200 written = 0; 4201 } 4202 else if (len > 0) 4203 written += len; 4204 } 4205 4206 // finished all the lines, close pipe 4207 CloseHandle(g_hChildStd_IN_Wr); 4208 return 0; 4209 } 4210 4211 4212 # define BUFLEN 100 // length for buffer, stolen from unix version 4213 4214 /* 4215 * This function read from the children's stdout and write the 4216 * data on screen or in the buffer accordingly. 4217 */ 4218 static void 4219 dump_pipe(int options, 4220 HANDLE g_hChildStd_OUT_Rd, 4221 garray_T *ga, 4222 char_u buffer[], 4223 DWORD *buffer_off) 4224 { 4225 DWORD availableBytes = 0; 4226 DWORD i; 4227 int ret; 4228 DWORD len; 4229 DWORD toRead; 4230 int repeatCount; 4231 4232 // we query the pipe to see if there is any data to read 4233 // to avoid to perform a blocking read 4234 ret = PeekNamedPipe(g_hChildStd_OUT_Rd, // pipe to query 4235 NULL, // optional buffer 4236 0, // buffer size 4237 NULL, // number of read bytes 4238 &availableBytes, // available bytes total 4239 NULL); // byteLeft 4240 4241 repeatCount = 0; 4242 // We got real data in the pipe, read it 4243 while (ret != 0 && availableBytes > 0) 4244 { 4245 repeatCount++; 4246 toRead = (DWORD)(BUFLEN - *buffer_off); 4247 toRead = availableBytes < toRead ? availableBytes : toRead; 4248 ReadFile(g_hChildStd_OUT_Rd, buffer + *buffer_off, toRead , &len, NULL); 4249 4250 // If we haven't read anything, there is a problem 4251 if (len == 0) 4252 break; 4253 4254 availableBytes -= len; 4255 4256 if (options & SHELL_READ) 4257 { 4258 // Do NUL -> NL translation, append NL separated 4259 // lines to the current buffer. 4260 for (i = 0; i < len; ++i) 4261 { 4262 if (buffer[i] == NL) 4263 append_ga_line(ga); 4264 else if (buffer[i] == NUL) 4265 ga_append(ga, NL); 4266 else 4267 ga_append(ga, buffer[i]); 4268 } 4269 } 4270 else if (has_mbyte) 4271 { 4272 int l; 4273 int c; 4274 char_u *p; 4275 4276 len += *buffer_off; 4277 buffer[len] = NUL; 4278 4279 // Check if the last character in buffer[] is 4280 // incomplete, keep these bytes for the next 4281 // round. 4282 for (p = buffer; p < buffer + len; p += l) 4283 { 4284 l = MB_CPTR2LEN(p); 4285 if (l == 0) 4286 l = 1; // NUL byte? 4287 else if (MB_BYTE2LEN(*p) != l) 4288 break; 4289 } 4290 if (p == buffer) // no complete character 4291 { 4292 // avoid getting stuck at an illegal byte 4293 if (len >= 12) 4294 ++p; 4295 else 4296 { 4297 *buffer_off = len; 4298 return; 4299 } 4300 } 4301 c = *p; 4302 *p = NUL; 4303 msg_puts((char *)buffer); 4304 if (p < buffer + len) 4305 { 4306 *p = c; 4307 *buffer_off = (DWORD)((buffer + len) - p); 4308 mch_memmove(buffer, p, *buffer_off); 4309 return; 4310 } 4311 *buffer_off = 0; 4312 } 4313 else 4314 { 4315 buffer[len] = NUL; 4316 msg_puts((char *)buffer); 4317 } 4318 4319 windgoto(msg_row, msg_col); 4320 cursor_on(); 4321 out_flush(); 4322 } 4323 } 4324 4325 /* 4326 * Version of system to use for windows NT > 5.0 (Win2K), use pipe 4327 * for communication and doesn't open any new window. 4328 */ 4329 static int 4330 mch_system_piped(char *cmd, int options) 4331 { 4332 STARTUPINFO si; 4333 PROCESS_INFORMATION pi; 4334 DWORD ret = 0; 4335 4336 HANDLE g_hChildStd_IN_Rd = NULL; 4337 HANDLE g_hChildStd_IN_Wr = NULL; 4338 HANDLE g_hChildStd_OUT_Rd = NULL; 4339 HANDLE g_hChildStd_OUT_Wr = NULL; 4340 4341 char_u buffer[BUFLEN + 1]; // reading buffer + size 4342 DWORD len; 4343 4344 // buffer used to receive keys 4345 char_u ta_buf[BUFLEN + 1]; // TypeAHead 4346 int ta_len = 0; // valid bytes in ta_buf[] 4347 4348 DWORD i; 4349 int noread_cnt = 0; 4350 garray_T ga; 4351 int delay = 1; 4352 DWORD buffer_off = 0; // valid bytes in buffer[] 4353 char *p = NULL; 4354 4355 SECURITY_ATTRIBUTES saAttr; 4356 4357 // Set the bInheritHandle flag so pipe handles are inherited. 4358 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 4359 saAttr.bInheritHandle = TRUE; 4360 saAttr.lpSecurityDescriptor = NULL; 4361 4362 if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) 4363 // Ensure the read handle to the pipe for STDOUT is not inherited. 4364 || ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) 4365 // Create a pipe for the child process's STDIN. 4366 || ! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0) 4367 // Ensure the write handle to the pipe for STDIN is not inherited. 4368 || ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) ) 4369 { 4370 CloseHandle(g_hChildStd_IN_Rd); 4371 CloseHandle(g_hChildStd_IN_Wr); 4372 CloseHandle(g_hChildStd_OUT_Rd); 4373 CloseHandle(g_hChildStd_OUT_Wr); 4374 msg_puts(_("\nCannot create pipes\n")); 4375 } 4376 4377 si.cb = sizeof(si); 4378 si.lpReserved = NULL; 4379 si.lpDesktop = NULL; 4380 si.lpTitle = NULL; 4381 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; 4382 4383 // set-up our file redirection 4384 si.hStdError = g_hChildStd_OUT_Wr; 4385 si.hStdOutput = g_hChildStd_OUT_Wr; 4386 si.hStdInput = g_hChildStd_IN_Rd; 4387 si.wShowWindow = SW_HIDE; 4388 si.cbReserved2 = 0; 4389 si.lpReserved2 = NULL; 4390 4391 if (options & SHELL_READ) 4392 ga_init2(&ga, 1, BUFLEN); 4393 4394 if (cmd != NULL) 4395 { 4396 p = (char *)vim_strsave((char_u *)cmd); 4397 if (p != NULL) 4398 unescape_shellxquote((char_u *)p, p_sxe); 4399 else 4400 p = cmd; 4401 } 4402 4403 // Now, run the command. 4404 // About "Inherit handles" being TRUE: this command can be litigious, 4405 // handle inheritance was deactivated for pending temp file, but, if we 4406 // deactivate it, the pipes don't work for some reason. 4407 vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE, 4408 &si, &pi, NULL, NULL); 4409 4410 if (p != cmd) 4411 vim_free(p); 4412 4413 // Close our unused side of the pipes 4414 CloseHandle(g_hChildStd_IN_Rd); 4415 CloseHandle(g_hChildStd_OUT_Wr); 4416 4417 if (options & SHELL_WRITE) 4418 { 4419 HANDLE thread = (HANDLE) 4420 _beginthreadex(NULL, // security attributes 4421 0, // default stack size 4422 sub_process_writer, // function to be executed 4423 g_hChildStd_IN_Wr, // parameter 4424 0, // creation flag, start immediately 4425 NULL); // we don't care about thread id 4426 CloseHandle(thread); 4427 g_hChildStd_IN_Wr = NULL; 4428 } 4429 4430 // Keep updating the window while waiting for the shell to finish. 4431 for (;;) 4432 { 4433 MSG msg; 4434 4435 if (pPeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE)) 4436 { 4437 TranslateMessage(&msg); 4438 pDispatchMessage(&msg); 4439 } 4440 4441 // write pipe information in the window 4442 if ((options & (SHELL_READ|SHELL_WRITE)) 4443 # ifdef FEAT_GUI 4444 || gui.in_use 4445 # endif 4446 ) 4447 { 4448 len = 0; 4449 if (!(options & SHELL_EXPAND) 4450 && ((options & 4451 (SHELL_READ|SHELL_WRITE|SHELL_COOKED)) 4452 != (SHELL_READ|SHELL_WRITE|SHELL_COOKED) 4453 # ifdef FEAT_GUI 4454 || gui.in_use 4455 # endif 4456 ) 4457 && (ta_len > 0 || noread_cnt > 4)) 4458 { 4459 if (ta_len == 0) 4460 { 4461 // Get extra characters when we don't have any. Reset the 4462 // counter and timer. 4463 noread_cnt = 0; 4464 len = ui_inchar(ta_buf, BUFLEN, 10L, 0); 4465 } 4466 if (ta_len > 0 || len > 0) 4467 { 4468 /* 4469 * For pipes: Check for CTRL-C: send interrupt signal to 4470 * child. Check for CTRL-D: EOF, close pipe to child. 4471 */ 4472 if (len == 1 && cmd != NULL) 4473 { 4474 if (ta_buf[ta_len] == Ctrl_C) 4475 { 4476 // Learn what exit code is expected, for 4477 // now put 9 as SIGKILL 4478 TerminateProcess(pi.hProcess, 9); 4479 } 4480 if (ta_buf[ta_len] == Ctrl_D) 4481 { 4482 CloseHandle(g_hChildStd_IN_Wr); 4483 g_hChildStd_IN_Wr = NULL; 4484 } 4485 } 4486 4487 term_replace_bs_del_keycode(ta_buf, ta_len, len); 4488 4489 /* 4490 * For pipes: echo the typed characters. For a pty this 4491 * does not seem to work. 4492 */ 4493 for (i = ta_len; i < ta_len + len; ++i) 4494 { 4495 if (ta_buf[i] == '\n' || ta_buf[i] == '\b') 4496 msg_putchar(ta_buf[i]); 4497 else if (has_mbyte) 4498 { 4499 int l = (*mb_ptr2len)(ta_buf + i); 4500 4501 msg_outtrans_len(ta_buf + i, l); 4502 i += l - 1; 4503 } 4504 else 4505 msg_outtrans_len(ta_buf + i, 1); 4506 } 4507 windgoto(msg_row, msg_col); 4508 out_flush(); 4509 4510 ta_len += len; 4511 4512 /* 4513 * Write the characters to the child, unless EOF has been 4514 * typed for pipes. Write one character at a time, to 4515 * avoid losing too much typeahead. When writing buffer 4516 * lines, drop the typed characters (only check for 4517 * CTRL-C). 4518 */ 4519 if (options & SHELL_WRITE) 4520 ta_len = 0; 4521 else if (g_hChildStd_IN_Wr != NULL) 4522 { 4523 WriteFile(g_hChildStd_IN_Wr, (char*)ta_buf, 4524 1, &len, NULL); 4525 // if we are typing in, we want to keep things reactive 4526 delay = 1; 4527 if (len > 0) 4528 { 4529 ta_len -= len; 4530 mch_memmove(ta_buf, ta_buf + len, ta_len); 4531 } 4532 } 4533 } 4534 } 4535 } 4536 4537 if (ta_len) 4538 ui_inchar_undo(ta_buf, ta_len); 4539 4540 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT) 4541 { 4542 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off); 4543 break; 4544 } 4545 4546 ++noread_cnt; 4547 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off); 4548 4549 // We start waiting for a very short time and then increase it, so 4550 // that we respond quickly when the process is quick, and don't 4551 // consume too much overhead when it's slow. 4552 if (delay < 50) 4553 delay += 10; 4554 } 4555 4556 // Close the pipe 4557 CloseHandle(g_hChildStd_OUT_Rd); 4558 if (g_hChildStd_IN_Wr != NULL) 4559 CloseHandle(g_hChildStd_IN_Wr); 4560 4561 WaitForSingleObject(pi.hProcess, INFINITE); 4562 4563 // Get the command exit code 4564 GetExitCodeProcess(pi.hProcess, &ret); 4565 4566 if (options & SHELL_READ) 4567 { 4568 if (ga.ga_len > 0) 4569 { 4570 append_ga_line(&ga); 4571 // remember that the NL was missing 4572 curbuf->b_no_eol_lnum = curwin->w_cursor.lnum; 4573 } 4574 else 4575 curbuf->b_no_eol_lnum = 0; 4576 ga_clear(&ga); 4577 } 4578 4579 // Close the handles to the subprocess, so that it goes away 4580 CloseHandle(pi.hThread); 4581 CloseHandle(pi.hProcess); 4582 4583 return ret; 4584 } 4585 4586 static int 4587 mch_system_g(char *cmd, int options) 4588 { 4589 // if we can pipe and the shelltemp option is off 4590 if (!p_stmp) 4591 return mch_system_piped(cmd, options); 4592 else 4593 return mch_system_classic(cmd, options); 4594 } 4595 #endif 4596 4597 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 4598 static int 4599 mch_system_c(char *cmd, int options UNUSED) 4600 { 4601 int ret; 4602 WCHAR *wcmd; 4603 char_u *buf; 4604 size_t len; 4605 4606 // If the command starts and ends with double quotes, enclose the command 4607 // in parentheses. 4608 len = STRLEN(cmd); 4609 if (len >= 2 && cmd[0] == '"' && cmd[len - 1] == '"') 4610 { 4611 len += 3; 4612 buf = alloc(len); 4613 if (buf == NULL) 4614 return -1; 4615 vim_snprintf((char *)buf, len, "(%s)", cmd); 4616 wcmd = enc_to_utf16(buf, NULL); 4617 free(buf); 4618 } 4619 else 4620 wcmd = enc_to_utf16((char_u *)cmd, NULL); 4621 4622 if (wcmd == NULL) 4623 return -1; 4624 4625 ret = _wsystem(wcmd); 4626 vim_free(wcmd); 4627 return ret; 4628 } 4629 4630 #endif 4631 4632 static int 4633 mch_system(char *cmd, int options) 4634 { 4635 #ifdef VIMDLL 4636 if (gui.in_use || gui.starting) 4637 return mch_system_g(cmd, options); 4638 else 4639 return mch_system_c(cmd, options); 4640 #elif defined(FEAT_GUI_MSWIN) 4641 return mch_system_g(cmd, options); 4642 #else 4643 return mch_system_c(cmd, options); 4644 #endif 4645 } 4646 4647 #if defined(FEAT_GUI) && defined(FEAT_TERMINAL) 4648 /* 4649 * Use a terminal window to run a shell command in. 4650 */ 4651 static int 4652 mch_call_shell_terminal( 4653 char_u *cmd, 4654 int options UNUSED) // SHELL_*, see vim.h 4655 { 4656 jobopt_T opt; 4657 char_u *newcmd = NULL; 4658 typval_T argvar[2]; 4659 long_u cmdlen; 4660 int retval = -1; 4661 buf_T *buf; 4662 job_T *job; 4663 aco_save_T aco; 4664 oparg_T oa; // operator arguments 4665 4666 if (cmd == NULL) 4667 cmdlen = STRLEN(p_sh) + 1; 4668 else 4669 cmdlen = STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10; 4670 newcmd = alloc(cmdlen); 4671 if (newcmd == NULL) 4672 return 255; 4673 if (cmd == NULL) 4674 { 4675 STRCPY(newcmd, p_sh); 4676 ch_log(NULL, "starting terminal to run a shell"); 4677 } 4678 else 4679 { 4680 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s", p_sh, p_shcf, cmd); 4681 ch_log(NULL, "starting terminal for system command '%s'", cmd); 4682 } 4683 4684 init_job_options(&opt); 4685 4686 argvar[0].v_type = VAR_STRING; 4687 argvar[0].vval.v_string = newcmd; 4688 argvar[1].v_type = VAR_UNKNOWN; 4689 buf = term_start(argvar, NULL, &opt, TERM_START_SYSTEM); 4690 if (buf == NULL) 4691 { 4692 vim_free(newcmd); 4693 return 255; 4694 } 4695 4696 job = term_getjob(buf->b_term); 4697 ++job->jv_refcount; 4698 4699 // Find a window to make "buf" curbuf. 4700 aucmd_prepbuf(&aco, buf); 4701 4702 clear_oparg(&oa); 4703 while (term_use_loop()) 4704 { 4705 if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active) 4706 { 4707 // If terminal_loop() returns OK we got a key that is handled 4708 // in Normal model. We don't do redrawing anyway. 4709 if (terminal_loop(TRUE) == OK) 4710 normal_cmd(&oa, TRUE); 4711 } 4712 else 4713 normal_cmd(&oa, TRUE); 4714 } 4715 retval = job->jv_exitval; 4716 ch_log(NULL, "system command finished"); 4717 4718 job_unref(job); 4719 4720 // restore curwin/curbuf and a few other things 4721 aucmd_restbuf(&aco); 4722 4723 wait_return(TRUE); 4724 do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE); 4725 4726 vim_free(newcmd); 4727 return retval; 4728 } 4729 #endif 4730 4731 /* 4732 * Either execute a command by calling the shell or start a new shell 4733 */ 4734 int 4735 mch_call_shell( 4736 char_u *cmd, 4737 int options) // SHELL_*, see vim.h 4738 { 4739 int x = 0; 4740 int tmode = cur_tmode; 4741 #ifdef FEAT_TITLE 4742 WCHAR szShellTitle[512]; 4743 4744 // Change the title to reflect that we are in a subshell. 4745 if (GetConsoleTitleW(szShellTitle, 4746 sizeof(szShellTitle)/sizeof(WCHAR) - 4) > 0) 4747 { 4748 if (cmd == NULL) 4749 wcscat(szShellTitle, L" :sh"); 4750 else 4751 { 4752 WCHAR *wn = enc_to_utf16((char_u *)cmd, NULL); 4753 4754 if (wn != NULL) 4755 { 4756 wcscat(szShellTitle, L" - !"); 4757 if ((wcslen(szShellTitle) + wcslen(wn) < 4758 sizeof(szShellTitle)/sizeof(WCHAR))) 4759 wcscat(szShellTitle, wn); 4760 SetConsoleTitleW(szShellTitle); 4761 vim_free(wn); 4762 } 4763 } 4764 } 4765 #endif 4766 4767 out_flush(); 4768 4769 #ifdef MCH_WRITE_DUMP 4770 if (fdDump) 4771 { 4772 fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options); 4773 fflush(fdDump); 4774 } 4775 #endif 4776 #if defined(FEAT_GUI) && defined(FEAT_TERMINAL) 4777 // TODO: make the terminal window work with input or output redirected. 4778 if ( 4779 # ifdef VIMDLL 4780 gui.in_use && 4781 # endif 4782 vim_strchr(p_go, GO_TERMINAL) != NULL 4783 && (options & (SHELL_FILTER|SHELL_DOOUT|SHELL_WRITE|SHELL_READ)) == 0) 4784 { 4785 char_u *cmdbase = cmd; 4786 4787 if (cmdbase != NULL) 4788 // Skip a leading quote and (. 4789 while (*cmdbase == '"' || *cmdbase == '(') 4790 ++cmdbase; 4791 4792 // Check the command does not begin with "start " 4793 if (cmdbase == NULL || STRNICMP(cmdbase, "start", 5) != 0 4794 || !VIM_ISWHITE(cmdbase[5])) 4795 { 4796 // Use a terminal window to run the command in. 4797 x = mch_call_shell_terminal(cmd, options); 4798 # ifdef FEAT_TITLE 4799 resettitle(); 4800 # endif 4801 return x; 4802 } 4803 } 4804 #endif 4805 4806 /* 4807 * Catch all deadly signals while running the external command, because a 4808 * CTRL-C, Ctrl-Break or illegal instruction might otherwise kill us. 4809 */ 4810 signal(SIGINT, SIG_IGN); 4811 #if defined(__GNUC__) && !defined(__MINGW32__) 4812 signal(SIGKILL, SIG_IGN); 4813 #else 4814 signal(SIGBREAK, SIG_IGN); 4815 #endif 4816 signal(SIGILL, SIG_IGN); 4817 signal(SIGFPE, SIG_IGN); 4818 signal(SIGSEGV, SIG_IGN); 4819 signal(SIGTERM, SIG_IGN); 4820 signal(SIGABRT, SIG_IGN); 4821 4822 if (options & SHELL_COOKED) 4823 settmode(TMODE_COOK); // set to normal mode 4824 4825 if (cmd == NULL) 4826 { 4827 x = mch_system((char *)p_sh, options); 4828 } 4829 else 4830 { 4831 // we use "command" or "cmd" to start the shell; slow but easy 4832 char_u *newcmd = NULL; 4833 char_u *cmdbase = cmd; 4834 long_u cmdlen; 4835 4836 // Skip a leading ", ( and "(. 4837 if (*cmdbase == '"' ) 4838 ++cmdbase; 4839 if (*cmdbase == '(') 4840 ++cmdbase; 4841 4842 if ((STRNICMP(cmdbase, "start", 5) == 0) && VIM_ISWHITE(cmdbase[5])) 4843 { 4844 STARTUPINFO si; 4845 PROCESS_INFORMATION pi; 4846 DWORD flags = CREATE_NEW_CONSOLE; 4847 INT n_show_cmd = SW_SHOWNORMAL; 4848 char_u *p; 4849 4850 ZeroMemory(&si, sizeof(si)); 4851 si.cb = sizeof(si); 4852 si.lpReserved = NULL; 4853 si.lpDesktop = NULL; 4854 si.lpTitle = NULL; 4855 si.dwFlags = 0; 4856 si.cbReserved2 = 0; 4857 si.lpReserved2 = NULL; 4858 4859 cmdbase = skipwhite(cmdbase + 5); 4860 if ((STRNICMP(cmdbase, "/min", 4) == 0) 4861 && VIM_ISWHITE(cmdbase[4])) 4862 { 4863 cmdbase = skipwhite(cmdbase + 4); 4864 si.dwFlags = STARTF_USESHOWWINDOW; 4865 si.wShowWindow = SW_SHOWMINNOACTIVE; 4866 n_show_cmd = SW_SHOWMINNOACTIVE; 4867 } 4868 else if ((STRNICMP(cmdbase, "/b", 2) == 0) 4869 && VIM_ISWHITE(cmdbase[2])) 4870 { 4871 cmdbase = skipwhite(cmdbase + 2); 4872 flags = CREATE_NO_WINDOW; 4873 si.dwFlags = STARTF_USESTDHANDLES; 4874 si.hStdInput = CreateFile("\\\\.\\NUL", // File name 4875 GENERIC_READ, // Access flags 4876 0, // Share flags 4877 NULL, // Security att. 4878 OPEN_EXISTING, // Open flags 4879 FILE_ATTRIBUTE_NORMAL, // File att. 4880 NULL); // Temp file 4881 si.hStdOutput = si.hStdInput; 4882 si.hStdError = si.hStdInput; 4883 } 4884 4885 // Remove a trailing ", ) and )" if they have a match 4886 // at the start of the command. 4887 if (cmdbase > cmd) 4888 { 4889 p = cmdbase + STRLEN(cmdbase); 4890 if (p > cmdbase && p[-1] == '"' && *cmd == '"') 4891 *--p = NUL; 4892 if (p > cmdbase && p[-1] == ')' 4893 && (*cmd =='(' || cmd[1] == '(')) 4894 *--p = NUL; 4895 } 4896 4897 newcmd = cmdbase; 4898 unescape_shellxquote(cmdbase, p_sxe); 4899 4900 /* 4901 * If creating new console, arguments are passed to the 4902 * 'cmd.exe' as-is. If it's not, arguments are not treated 4903 * correctly for current 'cmd.exe'. So unescape characters in 4904 * shellxescape except '|' for avoiding to be treated as 4905 * argument to them. Pass the arguments to sub-shell. 4906 */ 4907 if (flags != CREATE_NEW_CONSOLE) 4908 { 4909 char_u *subcmd; 4910 char_u *cmd_shell = mch_getenv("COMSPEC"); 4911 4912 if (cmd_shell == NULL || *cmd_shell == NUL) 4913 cmd_shell = (char_u *)default_shell(); 4914 4915 subcmd = vim_strsave_escaped_ext(cmdbase, 4916 (char_u *)"|", '^', FALSE); 4917 if (subcmd != NULL) 4918 { 4919 // make "cmd.exe /c arguments" 4920 cmdlen = STRLEN(cmd_shell) + STRLEN(subcmd) + 5; 4921 newcmd = alloc(cmdlen); 4922 if (newcmd != NULL) 4923 vim_snprintf((char *)newcmd, cmdlen, "%s /c %s", 4924 cmd_shell, subcmd); 4925 else 4926 newcmd = cmdbase; 4927 vim_free(subcmd); 4928 } 4929 } 4930 4931 /* 4932 * Now, start the command as a process, so that it doesn't 4933 * inherit our handles which causes unpleasant dangling swap 4934 * files if we exit before the spawned process 4935 */ 4936 if (vim_create_process((char *)newcmd, FALSE, flags, 4937 &si, &pi, NULL, NULL)) 4938 x = 0; 4939 else if (vim_shell_execute((char *)newcmd, n_show_cmd) 4940 > (HINSTANCE)32) 4941 x = 0; 4942 else 4943 { 4944 x = -1; 4945 #ifdef FEAT_GUI_MSWIN 4946 # ifdef VIMDLL 4947 if (gui.in_use) 4948 # endif 4949 emsg(_("E371: Command not found")); 4950 #endif 4951 } 4952 4953 if (newcmd != cmdbase) 4954 vim_free(newcmd); 4955 4956 if (si.dwFlags == STARTF_USESTDHANDLES && si.hStdInput != NULL) 4957 { 4958 // Close the handle to \\.\NUL created above. 4959 CloseHandle(si.hStdInput); 4960 } 4961 // Close the handles to the subprocess, so that it goes away 4962 CloseHandle(pi.hThread); 4963 CloseHandle(pi.hProcess); 4964 } 4965 else 4966 { 4967 cmdlen = 4968 #ifdef FEAT_GUI_MSWIN 4969 ((gui.in_use || gui.starting) ? 4970 (!s_dont_use_vimrun && p_stmp ? 4971 STRLEN(vimrun_path) : STRLEN(p_sh) + STRLEN(p_shcf)) 4972 : 0) + 4973 #endif 4974 STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10; 4975 4976 newcmd = alloc(cmdlen); 4977 if (newcmd != NULL) 4978 { 4979 #if defined(FEAT_GUI_MSWIN) 4980 if ( 4981 # ifdef VIMDLL 4982 (gui.in_use || gui.starting) && 4983 # endif 4984 need_vimrun_warning) 4985 { 4986 char *msg = _("VIMRUN.EXE not found in your $PATH.\n" 4987 "External commands will not pause after completion.\n" 4988 "See :help win32-vimrun for more information."); 4989 char *title = _("Vim Warning"); 4990 WCHAR *wmsg = enc_to_utf16((char_u *)msg, NULL); 4991 WCHAR *wtitle = enc_to_utf16((char_u *)title, NULL); 4992 4993 if (wmsg != NULL && wtitle != NULL) 4994 MessageBoxW(NULL, wmsg, wtitle, MB_ICONWARNING); 4995 vim_free(wmsg); 4996 vim_free(wtitle); 4997 need_vimrun_warning = FALSE; 4998 } 4999 if ( 5000 # ifdef VIMDLL 5001 (gui.in_use || gui.starting) && 5002 # endif 5003 !s_dont_use_vimrun && p_stmp) 5004 // Use vimrun to execute the command. It opens a console 5005 // window, which can be closed without killing Vim. 5006 vim_snprintf((char *)newcmd, cmdlen, "%s%s%s %s %s", 5007 vimrun_path, 5008 (msg_silent != 0 || (options & SHELL_DOOUT)) 5009 ? "-s " : "", 5010 p_sh, p_shcf, cmd); 5011 else if ( 5012 # ifdef VIMDLL 5013 (gui.in_use || gui.starting) && 5014 # endif 5015 s_dont_use_vimrun && STRCMP(p_shcf, "/c") == 0) 5016 // workaround for the case that "vimrun" does not exist 5017 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s %s %s", 5018 p_sh, p_shcf, p_sh, p_shcf, cmd); 5019 else 5020 #endif 5021 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s", 5022 p_sh, p_shcf, cmd); 5023 x = mch_system((char *)newcmd, options); 5024 vim_free(newcmd); 5025 } 5026 } 5027 } 5028 5029 if (tmode == TMODE_RAW) 5030 { 5031 // The shell may have messed with the mode, always set it. 5032 cur_tmode = TMODE_UNKNOWN; 5033 settmode(TMODE_RAW); // set to raw mode 5034 } 5035 5036 // Print the return value, unless "vimrun" was used. 5037 if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent 5038 #if defined(FEAT_GUI_MSWIN) 5039 && ((gui.in_use || gui.starting) ? 5040 ((options & SHELL_DOOUT) || s_dont_use_vimrun || !p_stmp) : 1) 5041 #endif 5042 ) 5043 { 5044 smsg(_("shell returned %d"), x); 5045 msg_putchar('\n'); 5046 } 5047 #ifdef FEAT_TITLE 5048 resettitle(); 5049 #endif 5050 5051 signal(SIGINT, SIG_DFL); 5052 #if defined(__GNUC__) && !defined(__MINGW32__) 5053 signal(SIGKILL, SIG_DFL); 5054 #else 5055 signal(SIGBREAK, SIG_DFL); 5056 #endif 5057 signal(SIGILL, SIG_DFL); 5058 signal(SIGFPE, SIG_DFL); 5059 signal(SIGSEGV, SIG_DFL); 5060 signal(SIGTERM, SIG_DFL); 5061 signal(SIGABRT, SIG_DFL); 5062 5063 return x; 5064 } 5065 5066 #if defined(FEAT_JOB_CHANNEL) || defined(PROTO) 5067 static HANDLE 5068 job_io_file_open( 5069 char_u *fname, 5070 DWORD dwDesiredAccess, 5071 DWORD dwShareMode, 5072 LPSECURITY_ATTRIBUTES lpSecurityAttributes, 5073 DWORD dwCreationDisposition, 5074 DWORD dwFlagsAndAttributes) 5075 { 5076 HANDLE h; 5077 WCHAR *wn; 5078 5079 wn = enc_to_utf16(fname, NULL); 5080 if (wn == NULL) 5081 return INVALID_HANDLE_VALUE; 5082 5083 h = CreateFileW(wn, dwDesiredAccess, dwShareMode, 5084 lpSecurityAttributes, dwCreationDisposition, 5085 dwFlagsAndAttributes, NULL); 5086 vim_free(wn); 5087 return h; 5088 } 5089 5090 /* 5091 * Turn the dictionary "env" into a NUL separated list that can be used as the 5092 * environment argument of vim_create_process(). 5093 */ 5094 void 5095 win32_build_env(dict_T *env, garray_T *gap, int is_terminal) 5096 { 5097 hashitem_T *hi; 5098 long_u todo = env != NULL ? env->dv_hashtab.ht_used : 0; 5099 LPVOID base = GetEnvironmentStringsW(); 5100 5101 // for last \0 5102 if (ga_grow(gap, 1) == FAIL) 5103 return; 5104 5105 if (env != NULL) 5106 { 5107 for (hi = env->dv_hashtab.ht_array; todo > 0; ++hi) 5108 { 5109 if (!HASHITEM_EMPTY(hi)) 5110 { 5111 typval_T *item = &dict_lookup(hi)->di_tv; 5112 WCHAR *wkey = enc_to_utf16((char_u *)hi->hi_key, NULL); 5113 WCHAR *wval = enc_to_utf16(tv_get_string(item), NULL); 5114 --todo; 5115 if (wkey != NULL && wval != NULL) 5116 { 5117 size_t n; 5118 size_t lkey = wcslen(wkey); 5119 size_t lval = wcslen(wval); 5120 5121 if (ga_grow(gap, (int)(lkey + lval + 2)) != OK) 5122 continue; 5123 for (n = 0; n < lkey; n++) 5124 *((WCHAR*)gap->ga_data + gap->ga_len++) = wkey[n]; 5125 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'='; 5126 for (n = 0; n < lval; n++) 5127 *((WCHAR*)gap->ga_data + gap->ga_len++) = wval[n]; 5128 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0'; 5129 } 5130 vim_free(wkey); 5131 vim_free(wval); 5132 } 5133 } 5134 } 5135 5136 if (base) 5137 { 5138 WCHAR *p = (WCHAR*) base; 5139 5140 // for last \0 5141 if (ga_grow(gap, 1) == FAIL) 5142 return; 5143 5144 while (*p != 0 || *(p + 1) != 0) 5145 { 5146 if (ga_grow(gap, 1) == OK) 5147 *((WCHAR*)gap->ga_data + gap->ga_len++) = *p; 5148 p++; 5149 } 5150 FreeEnvironmentStrings(base); 5151 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0'; 5152 } 5153 5154 # if defined(FEAT_CLIENTSERVER) || defined(FEAT_TERMINAL) 5155 { 5156 # ifdef FEAT_CLIENTSERVER 5157 char_u *servername = get_vim_var_str(VV_SEND_SERVER); 5158 size_t servername_len = STRLEN(servername); 5159 # endif 5160 # ifdef FEAT_TERMINAL 5161 char_u *version = get_vim_var_str(VV_VERSION); 5162 size_t version_len = STRLEN(version); 5163 # endif 5164 // size of "VIM_SERVERNAME=" and value, 5165 // plus "VIM_TERMINAL=" and value, 5166 // plus two terminating NULs 5167 size_t n = 0 5168 # ifdef FEAT_CLIENTSERVER 5169 + 15 + servername_len 5170 # endif 5171 # ifdef FEAT_TERMINAL 5172 + 13 + version_len + 2 5173 # endif 5174 ; 5175 5176 if (ga_grow(gap, (int)n) == OK) 5177 { 5178 # ifdef FEAT_CLIENTSERVER 5179 for (n = 0; n < 15; n++) 5180 *((WCHAR*)gap->ga_data + gap->ga_len++) = 5181 (WCHAR)"VIM_SERVERNAME="[n]; 5182 for (n = 0; n < servername_len; n++) 5183 *((WCHAR*)gap->ga_data + gap->ga_len++) = 5184 (WCHAR)servername[n]; 5185 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0'; 5186 # endif 5187 # ifdef FEAT_TERMINAL 5188 if (is_terminal) 5189 { 5190 for (n = 0; n < 13; n++) 5191 *((WCHAR*)gap->ga_data + gap->ga_len++) = 5192 (WCHAR)"VIM_TERMINAL="[n]; 5193 for (n = 0; n < version_len; n++) 5194 *((WCHAR*)gap->ga_data + gap->ga_len++) = 5195 (WCHAR)version[n]; 5196 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0'; 5197 } 5198 # endif 5199 } 5200 } 5201 # endif 5202 } 5203 5204 /* 5205 * Create a pair of pipes. 5206 * Return TRUE for success, FALSE for failure. 5207 */ 5208 static BOOL 5209 create_pipe_pair(HANDLE handles[2]) 5210 { 5211 static LONG s; 5212 char name[64]; 5213 SECURITY_ATTRIBUTES sa; 5214 5215 sprintf(name, "\\\\?\\pipe\\vim-%08lx-%08lx", 5216 GetCurrentProcessId(), 5217 InterlockedIncrement(&s)); 5218 5219 // Create named pipe. Max size of named pipe is 65535. 5220 handles[1] = CreateNamedPipe( 5221 name, 5222 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, 5223 PIPE_TYPE_BYTE | PIPE_NOWAIT, 5224 1, MAX_NAMED_PIPE_SIZE, 0, 0, NULL); 5225 5226 if (handles[1] == INVALID_HANDLE_VALUE) 5227 return FALSE; 5228 5229 sa.nLength = sizeof(sa); 5230 sa.bInheritHandle = TRUE; 5231 sa.lpSecurityDescriptor = NULL; 5232 5233 handles[0] = CreateFile(name, 5234 FILE_GENERIC_READ, 5235 FILE_SHARE_READ, &sa, 5236 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 5237 5238 if (handles[0] == INVALID_HANDLE_VALUE) 5239 { 5240 CloseHandle(handles[1]); 5241 return FALSE; 5242 } 5243 5244 return TRUE; 5245 } 5246 5247 void 5248 mch_job_start(char *cmd, job_T *job, jobopt_T *options) 5249 { 5250 STARTUPINFO si; 5251 PROCESS_INFORMATION pi; 5252 HANDLE jo; 5253 SECURITY_ATTRIBUTES saAttr; 5254 channel_T *channel = NULL; 5255 HANDLE ifd[2]; 5256 HANDLE ofd[2]; 5257 HANDLE efd[2]; 5258 garray_T ga; 5259 5260 int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL; 5261 int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL; 5262 int use_null_for_err = options->jo_io[PART_ERR] == JIO_NULL; 5263 int use_file_for_in = options->jo_io[PART_IN] == JIO_FILE; 5264 int use_file_for_out = options->jo_io[PART_OUT] == JIO_FILE; 5265 int use_file_for_err = options->jo_io[PART_ERR] == JIO_FILE; 5266 int use_out_for_err = options->jo_io[PART_ERR] == JIO_OUT; 5267 5268 if (use_out_for_err && use_null_for_out) 5269 use_null_for_err = TRUE; 5270 5271 ifd[0] = INVALID_HANDLE_VALUE; 5272 ifd[1] = INVALID_HANDLE_VALUE; 5273 ofd[0] = INVALID_HANDLE_VALUE; 5274 ofd[1] = INVALID_HANDLE_VALUE; 5275 efd[0] = INVALID_HANDLE_VALUE; 5276 efd[1] = INVALID_HANDLE_VALUE; 5277 ga_init2(&ga, (int)sizeof(wchar_t), 500); 5278 5279 jo = CreateJobObject(NULL, NULL); 5280 if (jo == NULL) 5281 { 5282 job->jv_status = JOB_FAILED; 5283 goto failed; 5284 } 5285 5286 if (options->jo_env != NULL) 5287 win32_build_env(options->jo_env, &ga, FALSE); 5288 5289 ZeroMemory(&pi, sizeof(pi)); 5290 ZeroMemory(&si, sizeof(si)); 5291 si.cb = sizeof(si); 5292 si.dwFlags |= STARTF_USESHOWWINDOW; 5293 si.wShowWindow = SW_HIDE; 5294 5295 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 5296 saAttr.bInheritHandle = TRUE; 5297 saAttr.lpSecurityDescriptor = NULL; 5298 5299 if (use_file_for_in) 5300 { 5301 char_u *fname = options->jo_io_name[PART_IN]; 5302 5303 ifd[0] = job_io_file_open(fname, GENERIC_READ, 5304 FILE_SHARE_READ | FILE_SHARE_WRITE, 5305 &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); 5306 if (ifd[0] == INVALID_HANDLE_VALUE) 5307 { 5308 semsg(_(e_notopen), fname); 5309 goto failed; 5310 } 5311 } 5312 else if (!use_null_for_in 5313 && (!create_pipe_pair(ifd) 5314 || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0))) 5315 goto failed; 5316 5317 if (use_file_for_out) 5318 { 5319 char_u *fname = options->jo_io_name[PART_OUT]; 5320 5321 ofd[1] = job_io_file_open(fname, GENERIC_WRITE, 5322 FILE_SHARE_READ | FILE_SHARE_WRITE, 5323 &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL); 5324 if (ofd[1] == INVALID_HANDLE_VALUE) 5325 { 5326 semsg(_(e_notopen), fname); 5327 goto failed; 5328 } 5329 } 5330 else if (!use_null_for_out && 5331 (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0) 5332 || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0))) 5333 goto failed; 5334 5335 if (use_file_for_err) 5336 { 5337 char_u *fname = options->jo_io_name[PART_ERR]; 5338 5339 efd[1] = job_io_file_open(fname, GENERIC_WRITE, 5340 FILE_SHARE_READ | FILE_SHARE_WRITE, 5341 &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL); 5342 if (efd[1] == INVALID_HANDLE_VALUE) 5343 { 5344 semsg(_(e_notopen), fname); 5345 goto failed; 5346 } 5347 } 5348 else if (!use_out_for_err && !use_null_for_err && 5349 (!CreatePipe(&efd[0], &efd[1], &saAttr, 0) 5350 || !SetHandleInformation(efd[0], HANDLE_FLAG_INHERIT, 0))) 5351 goto failed; 5352 5353 si.dwFlags |= STARTF_USESTDHANDLES; 5354 si.hStdInput = ifd[0]; 5355 si.hStdOutput = ofd[1]; 5356 si.hStdError = use_out_for_err ? ofd[1] : efd[1]; 5357 5358 if (!use_null_for_in || !use_null_for_out || !use_null_for_err) 5359 { 5360 if (options->jo_set & JO_CHANNEL) 5361 { 5362 channel = options->jo_channel; 5363 if (channel != NULL) 5364 ++channel->ch_refcount; 5365 } 5366 else 5367 channel = add_channel(); 5368 if (channel == NULL) 5369 goto failed; 5370 } 5371 5372 if (!vim_create_process(cmd, TRUE, 5373 CREATE_SUSPENDED | 5374 CREATE_DEFAULT_ERROR_MODE | 5375 CREATE_NEW_PROCESS_GROUP | 5376 CREATE_UNICODE_ENVIRONMENT | 5377 CREATE_NEW_CONSOLE, 5378 &si, &pi, 5379 ga.ga_data, 5380 (char *)options->jo_cwd)) 5381 { 5382 CloseHandle(jo); 5383 job->jv_status = JOB_FAILED; 5384 goto failed; 5385 } 5386 5387 ga_clear(&ga); 5388 5389 if (!AssignProcessToJobObject(jo, pi.hProcess)) 5390 { 5391 // if failing, switch the way to terminate 5392 // process with TerminateProcess. 5393 CloseHandle(jo); 5394 jo = NULL; 5395 } 5396 ResumeThread(pi.hThread); 5397 CloseHandle(pi.hThread); 5398 job->jv_proc_info = pi; 5399 job->jv_job_object = jo; 5400 job->jv_status = JOB_STARTED; 5401 5402 CloseHandle(ifd[0]); 5403 CloseHandle(ofd[1]); 5404 if (!use_out_for_err && !use_null_for_err) 5405 CloseHandle(efd[1]); 5406 5407 job->jv_channel = channel; 5408 if (channel != NULL) 5409 { 5410 channel_set_pipes(channel, 5411 use_file_for_in || use_null_for_in 5412 ? INVALID_FD : (sock_T)ifd[1], 5413 use_file_for_out || use_null_for_out 5414 ? INVALID_FD : (sock_T)ofd[0], 5415 use_out_for_err || use_file_for_err || use_null_for_err 5416 ? INVALID_FD : (sock_T)efd[0]); 5417 channel_set_job(channel, job, options); 5418 } 5419 return; 5420 5421 failed: 5422 CloseHandle(ifd[0]); 5423 CloseHandle(ofd[0]); 5424 CloseHandle(efd[0]); 5425 CloseHandle(ifd[1]); 5426 CloseHandle(ofd[1]); 5427 CloseHandle(efd[1]); 5428 channel_unref(channel); 5429 ga_clear(&ga); 5430 } 5431 5432 char * 5433 mch_job_status(job_T *job) 5434 { 5435 DWORD dwExitCode = 0; 5436 5437 if (!GetExitCodeProcess(job->jv_proc_info.hProcess, &dwExitCode) 5438 || dwExitCode != STILL_ACTIVE) 5439 { 5440 job->jv_exitval = (int)dwExitCode; 5441 if (job->jv_status < JOB_ENDED) 5442 { 5443 ch_log(job->jv_channel, "Job ended"); 5444 job->jv_status = JOB_ENDED; 5445 } 5446 return "dead"; 5447 } 5448 return "run"; 5449 } 5450 5451 job_T * 5452 mch_detect_ended_job(job_T *job_list) 5453 { 5454 HANDLE jobHandles[MAXIMUM_WAIT_OBJECTS]; 5455 job_T *jobArray[MAXIMUM_WAIT_OBJECTS]; 5456 job_T *job = job_list; 5457 5458 while (job != NULL) 5459 { 5460 DWORD n; 5461 DWORD result; 5462 5463 for (n = 0; n < MAXIMUM_WAIT_OBJECTS 5464 && job != NULL; job = job->jv_next) 5465 { 5466 if (job->jv_status == JOB_STARTED) 5467 { 5468 jobHandles[n] = job->jv_proc_info.hProcess; 5469 jobArray[n] = job; 5470 ++n; 5471 } 5472 } 5473 if (n == 0) 5474 continue; 5475 result = WaitForMultipleObjects(n, jobHandles, FALSE, 0); 5476 if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + n) 5477 { 5478 job_T *wait_job = jobArray[result - WAIT_OBJECT_0]; 5479 5480 if (STRCMP(mch_job_status(wait_job), "dead") == 0) 5481 return wait_job; 5482 } 5483 } 5484 return NULL; 5485 } 5486 5487 static BOOL 5488 terminate_all(HANDLE process, int code) 5489 { 5490 PROCESSENTRY32 pe; 5491 HANDLE h = INVALID_HANDLE_VALUE; 5492 DWORD pid = GetProcessId(process); 5493 5494 if (pid != 0) 5495 { 5496 h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 5497 if (h != INVALID_HANDLE_VALUE) 5498 { 5499 pe.dwSize = sizeof(PROCESSENTRY32); 5500 if (!Process32First(h, &pe)) 5501 goto theend; 5502 5503 do 5504 { 5505 if (pe.th32ParentProcessID == pid) 5506 { 5507 HANDLE ph = OpenProcess( 5508 PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID); 5509 if (ph != NULL) 5510 { 5511 terminate_all(ph, code); 5512 CloseHandle(ph); 5513 } 5514 } 5515 } while (Process32Next(h, &pe)); 5516 5517 CloseHandle(h); 5518 } 5519 } 5520 5521 theend: 5522 return TerminateProcess(process, code); 5523 } 5524 5525 /* 5526 * Send a (deadly) signal to "job". 5527 * Return FAIL if it didn't work. 5528 */ 5529 int 5530 mch_signal_job(job_T *job, char_u *how) 5531 { 5532 int ret; 5533 5534 if (STRCMP(how, "term") == 0 || STRCMP(how, "kill") == 0 || *how == NUL) 5535 { 5536 // deadly signal 5537 if (job->jv_job_object != NULL) 5538 { 5539 if (job->jv_channel != NULL && job->jv_channel->ch_anonymous_pipe) 5540 job->jv_channel->ch_killing = TRUE; 5541 return TerminateJobObject(job->jv_job_object, -1) ? OK : FAIL; 5542 } 5543 return terminate_all(job->jv_proc_info.hProcess, -1) ? OK : FAIL; 5544 } 5545 5546 if (!AttachConsole(job->jv_proc_info.dwProcessId)) 5547 return FAIL; 5548 ret = GenerateConsoleCtrlEvent( 5549 STRCMP(how, "int") == 0 ? CTRL_C_EVENT : CTRL_BREAK_EVENT, 5550 job->jv_proc_info.dwProcessId) 5551 ? OK : FAIL; 5552 FreeConsole(); 5553 return ret; 5554 } 5555 5556 /* 5557 * Clear the data related to "job". 5558 */ 5559 void 5560 mch_clear_job(job_T *job) 5561 { 5562 if (job->jv_status != JOB_FAILED) 5563 { 5564 if (job->jv_job_object != NULL) 5565 CloseHandle(job->jv_job_object); 5566 CloseHandle(job->jv_proc_info.hProcess); 5567 } 5568 } 5569 #endif 5570 5571 5572 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 5573 5574 /* 5575 * Start termcap mode 5576 */ 5577 static void 5578 termcap_mode_start(void) 5579 { 5580 DWORD cmodein; 5581 5582 if (g_fTermcapMode) 5583 return; 5584 5585 if (!p_rs && USE_VTP) 5586 vtp_printf("\033[?1049h"); 5587 5588 SaveConsoleBuffer(&g_cbNonTermcap); 5589 5590 if (g_cbTermcap.IsValid) 5591 { 5592 /* 5593 * We've been in termcap mode before. Restore certain screen 5594 * characteristics, including the buffer size and the window 5595 * size. Since we will be redrawing the screen, we don't need 5596 * to restore the actual contents of the buffer. 5597 */ 5598 RestoreConsoleBuffer(&g_cbTermcap, FALSE); 5599 reset_console_color_rgb(); 5600 SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow); 5601 Rows = g_cbTermcap.Info.dwSize.Y; 5602 Columns = g_cbTermcap.Info.dwSize.X; 5603 } 5604 else 5605 { 5606 /* 5607 * This is our first time entering termcap mode. Clear the console 5608 * screen buffer, and resize the buffer to match the current window 5609 * size. We will use this as the size of our editing environment. 5610 */ 5611 ClearConsoleBuffer(g_attrCurrent); 5612 set_console_color_rgb(); 5613 ResizeConBufAndWindow(g_hConOut, Columns, Rows); 5614 } 5615 5616 # ifdef FEAT_TITLE 5617 resettitle(); 5618 # endif 5619 5620 GetConsoleMode(g_hConIn, &cmodein); 5621 if (g_fMouseActive) 5622 cmodein |= ENABLE_MOUSE_INPUT; 5623 else 5624 cmodein &= ~ENABLE_MOUSE_INPUT; 5625 cmodein |= ENABLE_WINDOW_INPUT; 5626 SetConsoleMode(g_hConIn, cmodein); 5627 5628 redraw_later_clear(); 5629 g_fTermcapMode = TRUE; 5630 } 5631 5632 5633 /* 5634 * End termcap mode 5635 */ 5636 static void 5637 termcap_mode_end(void) 5638 { 5639 DWORD cmodein; 5640 ConsoleBuffer *cb; 5641 COORD coord; 5642 DWORD dwDummy; 5643 5644 if (!g_fTermcapMode) 5645 return; 5646 5647 SaveConsoleBuffer(&g_cbTermcap); 5648 5649 GetConsoleMode(g_hConIn, &cmodein); 5650 cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT); 5651 SetConsoleMode(g_hConIn, cmodein); 5652 5653 # ifdef FEAT_RESTORE_ORIG_SCREEN 5654 cb = exiting ? &g_cbOrig : &g_cbNonTermcap; 5655 # else 5656 cb = &g_cbNonTermcap; 5657 # endif 5658 RestoreConsoleBuffer(cb, p_rs); 5659 restore_console_color_rgb(); 5660 SetConsoleCursorInfo(g_hConOut, &g_cci); 5661 5662 if (p_rs || exiting) 5663 { 5664 /* 5665 * Clear anything that happens to be on the current line. 5666 */ 5667 coord.X = 0; 5668 coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1)); 5669 FillConsoleOutputCharacter(g_hConOut, ' ', 5670 cb->Info.dwSize.X, coord, &dwDummy); 5671 /* 5672 * The following is just for aesthetics. If we are exiting without 5673 * restoring the screen, then we want to have a prompt string 5674 * appear at the bottom line. However, the command interpreter 5675 * seems to always advance the cursor one line before displaying 5676 * the prompt string, which causes the screen to scroll. To 5677 * counter this, move the cursor up one line before exiting. 5678 */ 5679 if (exiting && !p_rs) 5680 coord.Y--; 5681 /* 5682 * Position the cursor at the leftmost column of the desired row. 5683 */ 5684 SetConsoleCursorPosition(g_hConOut, coord); 5685 } 5686 5687 if (!p_rs && USE_VTP) 5688 vtp_printf("\033[?1049l"); 5689 5690 g_fTermcapMode = FALSE; 5691 } 5692 #endif // FEAT_GUI_MSWIN 5693 5694 5695 #if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL) 5696 void 5697 mch_write( 5698 char_u *s UNUSED, 5699 int len UNUSED) 5700 { 5701 // never used 5702 } 5703 5704 #else 5705 5706 /* 5707 * clear `n' chars, starting from `coord' 5708 */ 5709 static void 5710 clear_chars( 5711 COORD coord, 5712 DWORD n) 5713 { 5714 if (!USE_VTP) 5715 { 5716 DWORD dwDummy; 5717 5718 FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy); 5719 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, 5720 &dwDummy); 5721 } 5722 else 5723 { 5724 set_console_color_rgb(); 5725 gotoxy(coord.X + 1, coord.Y + 1); 5726 vtp_printf("\033[%dX", n); 5727 } 5728 } 5729 5730 5731 /* 5732 * Clear the screen 5733 */ 5734 static void 5735 clear_screen(void) 5736 { 5737 g_coord.X = g_coord.Y = 0; 5738 5739 if (!USE_VTP) 5740 clear_chars(g_coord, Rows * Columns); 5741 else 5742 { 5743 set_console_color_rgb(); 5744 gotoxy(1, 1); 5745 vtp_printf("\033[2J"); 5746 } 5747 } 5748 5749 5750 /* 5751 * Clear to end of display 5752 */ 5753 static void 5754 clear_to_end_of_display(void) 5755 { 5756 COORD save = g_coord; 5757 5758 if (!USE_VTP) 5759 clear_chars(g_coord, (Rows - g_coord.Y - 1) 5760 * Columns + (Columns - g_coord.X)); 5761 else 5762 { 5763 set_console_color_rgb(); 5764 gotoxy(g_coord.X + 1, g_coord.Y + 1); 5765 vtp_printf("\033[0J"); 5766 5767 gotoxy(save.X + 1, save.Y + 1); 5768 g_coord = save; 5769 } 5770 } 5771 5772 5773 /* 5774 * Clear to end of line 5775 */ 5776 static void 5777 clear_to_end_of_line(void) 5778 { 5779 COORD save = g_coord; 5780 5781 if (!USE_VTP) 5782 clear_chars(g_coord, Columns - g_coord.X); 5783 else 5784 { 5785 set_console_color_rgb(); 5786 gotoxy(g_coord.X + 1, g_coord.Y + 1); 5787 vtp_printf("\033[0K"); 5788 5789 gotoxy(save.X + 1, save.Y + 1); 5790 g_coord = save; 5791 } 5792 } 5793 5794 5795 /* 5796 * Scroll the scroll region up by `cLines' lines 5797 */ 5798 static void 5799 scroll(unsigned cLines) 5800 { 5801 COORD oldcoord = g_coord; 5802 5803 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1); 5804 delete_lines(cLines); 5805 5806 g_coord = oldcoord; 5807 } 5808 5809 5810 /* 5811 * Set the scroll region 5812 */ 5813 static void 5814 set_scroll_region( 5815 unsigned left, 5816 unsigned top, 5817 unsigned right, 5818 unsigned bottom) 5819 { 5820 if (left >= right 5821 || top >= bottom 5822 || right > (unsigned) Columns - 1 5823 || bottom > (unsigned) Rows - 1) 5824 return; 5825 5826 g_srScrollRegion.Left = left; 5827 g_srScrollRegion.Top = top; 5828 g_srScrollRegion.Right = right; 5829 g_srScrollRegion.Bottom = bottom; 5830 } 5831 5832 static void 5833 set_scroll_region_tb( 5834 unsigned top, 5835 unsigned bottom) 5836 { 5837 if (top >= bottom || bottom > (unsigned)Rows - 1) 5838 return; 5839 5840 g_srScrollRegion.Top = top; 5841 g_srScrollRegion.Bottom = bottom; 5842 } 5843 5844 static void 5845 set_scroll_region_lr( 5846 unsigned left, 5847 unsigned right) 5848 { 5849 if (left >= right || right > (unsigned)Columns - 1) 5850 return; 5851 5852 g_srScrollRegion.Left = left; 5853 g_srScrollRegion.Right = right; 5854 } 5855 5856 5857 /* 5858 * Insert `cLines' lines at the current cursor position 5859 */ 5860 static void 5861 insert_lines(unsigned cLines) 5862 { 5863 SMALL_RECT source, clip; 5864 COORD dest; 5865 CHAR_INFO fill; 5866 5867 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1); 5868 5869 dest.X = g_srScrollRegion.Left; 5870 dest.Y = g_coord.Y + cLines; 5871 5872 source.Left = g_srScrollRegion.Left; 5873 source.Top = g_coord.Y; 5874 source.Right = g_srScrollRegion.Right; 5875 source.Bottom = g_srScrollRegion.Bottom - cLines; 5876 5877 clip.Left = g_srScrollRegion.Left; 5878 clip.Top = g_coord.Y; 5879 clip.Right = g_srScrollRegion.Right; 5880 clip.Bottom = g_srScrollRegion.Bottom; 5881 5882 fill.Char.AsciiChar = ' '; 5883 if (!USE_VTP) 5884 fill.Attributes = g_attrCurrent; 5885 else 5886 fill.Attributes = g_attrDefault; 5887 5888 set_console_color_rgb(); 5889 5890 ScrollConsoleScreenBuffer(g_hConOut, &source, &clip, dest, &fill); 5891 5892 // Here we have to deal with a win32 console flake: If the scroll 5893 // region looks like abc and we scroll c to a and fill with d we get 5894 // cbd... if we scroll block c one line at a time to a, we get cdd... 5895 // vim expects cdd consistently... So we have to deal with that 5896 // here... (this also occurs scrolling the same way in the other 5897 // direction). 5898 5899 if (source.Bottom < dest.Y) 5900 { 5901 COORD coord; 5902 int i; 5903 5904 coord.X = source.Left; 5905 for (i = clip.Top; i < dest.Y; ++i) 5906 { 5907 coord.Y = i; 5908 clear_chars(coord, source.Right - source.Left + 1); 5909 } 5910 } 5911 5912 if (USE_WT) 5913 { 5914 COORD coord; 5915 int i; 5916 5917 coord.X = source.Left; 5918 for (i = source.Top; i < dest.Y; ++i) 5919 { 5920 coord.Y = i; 5921 clear_chars(coord, source.Right - source.Left + 1); 5922 } 5923 } 5924 } 5925 5926 5927 /* 5928 * Delete `cLines' lines at the current cursor position 5929 */ 5930 static void 5931 delete_lines(unsigned cLines) 5932 { 5933 SMALL_RECT source, clip; 5934 COORD dest; 5935 CHAR_INFO fill; 5936 int nb; 5937 5938 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1); 5939 5940 dest.X = g_srScrollRegion.Left; 5941 dest.Y = g_coord.Y; 5942 5943 source.Left = g_srScrollRegion.Left; 5944 source.Top = g_coord.Y + cLines; 5945 source.Right = g_srScrollRegion.Right; 5946 source.Bottom = g_srScrollRegion.Bottom; 5947 5948 clip.Left = g_srScrollRegion.Left; 5949 clip.Top = g_coord.Y; 5950 clip.Right = g_srScrollRegion.Right; 5951 clip.Bottom = g_srScrollRegion.Bottom; 5952 5953 fill.Char.AsciiChar = ' '; 5954 if (!USE_VTP) 5955 fill.Attributes = g_attrCurrent; 5956 else 5957 fill.Attributes = g_attrDefault; 5958 5959 set_console_color_rgb(); 5960 5961 ScrollConsoleScreenBuffer(g_hConOut, &source, &clip, dest, &fill); 5962 5963 // Here we have to deal with a win32 console flake; See insert_lines() 5964 // above. 5965 5966 nb = dest.Y + (source.Bottom - source.Top) + 1; 5967 5968 if (nb < source.Top) 5969 { 5970 COORD coord; 5971 int i; 5972 5973 coord.X = source.Left; 5974 for (i = nb; i < clip.Bottom; ++i) 5975 { 5976 coord.Y = i; 5977 clear_chars(coord, source.Right - source.Left + 1); 5978 } 5979 } 5980 5981 if (USE_WT) 5982 { 5983 COORD coord; 5984 int i; 5985 5986 coord.X = source.Left; 5987 for (i = nb; i <= source.Bottom; ++i) 5988 { 5989 coord.Y = i; 5990 clear_chars(coord, source.Right - source.Left + 1); 5991 } 5992 } 5993 } 5994 5995 5996 /* 5997 * Set the cursor position to (x,y) (1-based). 5998 */ 5999 static void 6000 gotoxy( 6001 unsigned x, 6002 unsigned y) 6003 { 6004 if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows) 6005 return; 6006 6007 if (!USE_VTP) 6008 { 6009 // There are reports of double-width characters not displayed 6010 // correctly. This workaround should fix it, similar to how it's done 6011 // for VTP. 6012 g_coord.X = 0; 6013 SetConsoleCursorPosition(g_hConOut, g_coord); 6014 6015 // external cursor coords are 1-based; internal are 0-based 6016 g_coord.X = x - 1; 6017 g_coord.Y = y - 1; 6018 SetConsoleCursorPosition(g_hConOut, g_coord); 6019 } 6020 else 6021 { 6022 // Move the cursor to the left edge of the screen to prevent screen 6023 // destruction. Insider build bug. Always enabled because it's cheap 6024 // and avoids mistakes with recognizing the build. 6025 vtp_printf("\033[%d;%dH", g_coord.Y + 1, 1); 6026 6027 vtp_printf("\033[%d;%dH", y, x); 6028 6029 g_coord.X = x - 1; 6030 g_coord.Y = y - 1; 6031 } 6032 } 6033 6034 6035 /* 6036 * Set the current text attribute = (foreground | background) 6037 * See ../runtime/doc/os_win32.txt for the numbers. 6038 */ 6039 static void 6040 textattr(WORD wAttr) 6041 { 6042 g_attrCurrent = wAttr & 0xff; 6043 6044 SetConsoleTextAttribute(g_hConOut, wAttr); 6045 } 6046 6047 6048 static void 6049 textcolor(WORD wAttr) 6050 { 6051 g_attrCurrent = (g_attrCurrent & 0xf0) + (wAttr & 0x0f); 6052 6053 if (!USE_VTP) 6054 SetConsoleTextAttribute(g_hConOut, g_attrCurrent); 6055 else 6056 vtp_sgr_bulk(wAttr); 6057 } 6058 6059 6060 static void 6061 textbackground(WORD wAttr) 6062 { 6063 g_attrCurrent = (g_attrCurrent & 0x0f) + ((wAttr & 0x0f) << 4); 6064 6065 if (!USE_VTP) 6066 SetConsoleTextAttribute(g_hConOut, g_attrCurrent); 6067 else 6068 vtp_sgr_bulk(wAttr); 6069 } 6070 6071 6072 /* 6073 * restore the default text attribute (whatever we started with) 6074 */ 6075 static void 6076 normvideo(void) 6077 { 6078 if (!USE_VTP) 6079 textattr(g_attrDefault); 6080 else 6081 vtp_sgr_bulk(0); 6082 } 6083 6084 6085 static WORD g_attrPreStandout = 0; 6086 6087 /* 6088 * Make the text standout, by brightening it 6089 */ 6090 static void 6091 standout(void) 6092 { 6093 g_attrPreStandout = g_attrCurrent; 6094 6095 textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY)); 6096 } 6097 6098 6099 /* 6100 * Turn off standout mode 6101 */ 6102 static void 6103 standend(void) 6104 { 6105 if (g_attrPreStandout) 6106 textattr(g_attrPreStandout); 6107 6108 g_attrPreStandout = 0; 6109 } 6110 6111 6112 /* 6113 * Set normal fg/bg color, based on T_ME. Called when t_me has been set. 6114 */ 6115 void 6116 mch_set_normal_colors(void) 6117 { 6118 char_u *p; 6119 int n; 6120 6121 cterm_normal_fg_color = (g_attrDefault & 0xf) + 1; 6122 cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1; 6123 if ( 6124 # ifdef FEAT_TERMGUICOLORS 6125 !p_tgc && 6126 # endif 6127 T_ME[0] == ESC && T_ME[1] == '|') 6128 { 6129 p = T_ME + 2; 6130 n = getdigits(&p); 6131 if (*p == 'm' && n > 0) 6132 { 6133 cterm_normal_fg_color = (n & 0xf) + 1; 6134 cterm_normal_bg_color = ((n >> 4) & 0xf) + 1; 6135 } 6136 } 6137 # ifdef FEAT_TERMGUICOLORS 6138 cterm_normal_fg_gui_color = INVALCOLOR; 6139 cterm_normal_bg_gui_color = INVALCOLOR; 6140 # endif 6141 } 6142 6143 6144 /* 6145 * visual bell: flash the screen 6146 */ 6147 static void 6148 visual_bell(void) 6149 { 6150 COORD coordOrigin = {0, 0}; 6151 WORD attrFlash = ~g_attrCurrent & 0xff; 6152 6153 DWORD dwDummy; 6154 LPWORD oldattrs = ALLOC_MULT(WORD, Rows * Columns); 6155 6156 if (oldattrs == NULL) 6157 return; 6158 ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns, 6159 coordOrigin, &dwDummy); 6160 FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns, 6161 coordOrigin, &dwDummy); 6162 6163 Sleep(15); // wait for 15 msec 6164 if (!USE_VTP) 6165 WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns, 6166 coordOrigin, &dwDummy); 6167 vim_free(oldattrs); 6168 } 6169 6170 6171 /* 6172 * Make the cursor visible or invisible 6173 */ 6174 static void 6175 cursor_visible(BOOL fVisible) 6176 { 6177 s_cursor_visible = fVisible; 6178 6179 if (USE_VTP) 6180 vtp_printf("\033[?25%c", fVisible ? 'h' : 'l'); 6181 6182 # ifdef MCH_CURSOR_SHAPE 6183 mch_update_cursor(); 6184 # endif 6185 } 6186 6187 6188 /* 6189 * Write "cbToWrite" bytes in `pchBuf' to the screen. 6190 * Returns the number of bytes actually written (at least one). 6191 */ 6192 static DWORD 6193 write_chars( 6194 char_u *pchBuf, 6195 DWORD cbToWrite) 6196 { 6197 COORD coord = g_coord; 6198 DWORD written; 6199 DWORD n, cchwritten; 6200 static DWORD cells; 6201 static WCHAR *unicodebuf = NULL; 6202 static int unibuflen = 0; 6203 static int length; 6204 int cp = enc_utf8 ? CP_UTF8 : enc_codepage; 6205 static WCHAR *utf8spbuf = NULL; 6206 static int utf8splength; 6207 static DWORD utf8spcells; 6208 static WCHAR **utf8usingbuf = &unicodebuf; 6209 6210 if (cbToWrite != 1 || *pchBuf != ' ' || !enc_utf8) 6211 { 6212 utf8usingbuf = &unicodebuf; 6213 do 6214 { 6215 length = MultiByteToWideChar(cp, 0, (LPCSTR)pchBuf, cbToWrite, 6216 unicodebuf, unibuflen); 6217 if (length && length <= unibuflen) 6218 break; 6219 vim_free(unicodebuf); 6220 unicodebuf = length ? LALLOC_MULT(WCHAR, length) : NULL; 6221 unibuflen = unibuflen ? 0 : length; 6222 } while(1); 6223 cells = mb_string2cells(pchBuf, cbToWrite); 6224 } 6225 else // cbToWrite == 1 && *pchBuf == ' ' && enc_utf8 6226 { 6227 if (utf8usingbuf != &utf8spbuf) 6228 { 6229 if (utf8spbuf == NULL) 6230 { 6231 cells = mb_string2cells((char_u *)" ", 1); 6232 length = MultiByteToWideChar(CP_UTF8, 0, " ", 1, NULL, 0); 6233 utf8spbuf = LALLOC_MULT(WCHAR, length); 6234 if (utf8spbuf != NULL) 6235 { 6236 MultiByteToWideChar(CP_UTF8, 0, " ", 1, utf8spbuf, length); 6237 utf8usingbuf = &utf8spbuf; 6238 utf8splength = length; 6239 utf8spcells = cells; 6240 } 6241 } 6242 else 6243 { 6244 utf8usingbuf = &utf8spbuf; 6245 length = utf8splength; 6246 cells = utf8spcells; 6247 } 6248 } 6249 } 6250 6251 if (!USE_VTP) 6252 { 6253 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cells, 6254 coord, &written); 6255 // When writing fails or didn't write a single character, pretend one 6256 // character was written, otherwise we get stuck. 6257 if (WriteConsoleOutputCharacterW(g_hConOut, *utf8usingbuf, length, 6258 coord, &cchwritten) == 0 6259 || cchwritten == 0 || cchwritten == (DWORD)-1) 6260 cchwritten = 1; 6261 } 6262 else 6263 { 6264 if (WriteConsoleW(g_hConOut, *utf8usingbuf, length, &cchwritten, 6265 NULL) == 0 || cchwritten == 0) 6266 cchwritten = 1; 6267 } 6268 6269 if (cchwritten == length) 6270 { 6271 written = cbToWrite; 6272 g_coord.X += (SHORT)cells; 6273 } 6274 else 6275 { 6276 char_u *p = pchBuf; 6277 for (n = 0; n < cchwritten; n++) 6278 MB_CPTR_ADV(p); 6279 written = p - pchBuf; 6280 g_coord.X += (SHORT)mb_string2cells(pchBuf, written); 6281 } 6282 6283 while (g_coord.X > g_srScrollRegion.Right) 6284 { 6285 g_coord.X -= (SHORT) Columns; 6286 if (g_coord.Y < g_srScrollRegion.Bottom) 6287 g_coord.Y++; 6288 } 6289 6290 // Cursor under VTP is always in the correct position, no need to reset. 6291 if (!USE_VTP) 6292 gotoxy(g_coord.X + 1, g_coord.Y + 1); 6293 6294 return written; 6295 } 6296 6297 static char_u * 6298 get_seq( 6299 int *args, 6300 int *count, 6301 char_u *head) 6302 { 6303 int argc; 6304 char_u *p; 6305 6306 if (head == NULL || *head != '\033') 6307 return NULL; 6308 6309 argc = 0; 6310 p = head; 6311 ++p; 6312 do 6313 { 6314 ++p; 6315 args[argc] = getdigits(&p); 6316 argc += (argc < 15) ? 1 : 0; 6317 } while (*p == ';'); 6318 *count = argc; 6319 6320 return p; 6321 } 6322 6323 static char_u * 6324 get_sgr( 6325 int *args, 6326 int *count, 6327 char_u *head) 6328 { 6329 char_u *p = get_seq(args, count, head); 6330 6331 return (p && *p == 'm') ? ++p : NULL; 6332 } 6333 6334 /* 6335 * Pointer to next if SGR (^[[n;2;*;*;*m), NULL otherwise. 6336 */ 6337 static char_u * 6338 sgrn2( 6339 char_u *head, 6340 int n) 6341 { 6342 int argc; 6343 int args[16]; 6344 char_u *p = get_sgr(args, &argc, head); 6345 6346 return p && argc == 5 && args[0] == n && args[1] == 2 ? p : NULL; 6347 } 6348 6349 /* 6350 * Pointer to next if SGR(^[[nm)<space>ESC, NULL otherwise. 6351 */ 6352 static char_u * 6353 sgrnc( 6354 char_u *head, 6355 int n) 6356 { 6357 int argc; 6358 int args[16]; 6359 char_u *p = get_sgr(args, &argc, head); 6360 6361 return p && argc == 1 && args[0] == n && (p = skipwhite(p)) && *p == '\033' 6362 ? p : NULL; 6363 } 6364 6365 static char_u * 6366 skipblank(char_u *q) 6367 { 6368 char_u *p = q; 6369 6370 while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') 6371 ++p; 6372 return p; 6373 } 6374 6375 /* 6376 * Pointer to the next if any whitespace that may follow SGR is ESC, otherwise 6377 * NULL. 6378 */ 6379 static char_u * 6380 sgrn2c( 6381 char_u *head, 6382 int n) 6383 { 6384 char_u *p = sgrn2(head, n); 6385 6386 return p && *p != NUL && (p = skipblank(p)) && *p == '\033' ? p : NULL; 6387 } 6388 6389 /* 6390 * If there is only a newline between the sequence immediately following it, 6391 * a pointer to the character following the newline is returned. 6392 * Otherwise NULL. 6393 */ 6394 static char_u * 6395 sgrn2cn( 6396 char_u *head, 6397 int n) 6398 { 6399 char_u *p = sgrn2(head, n); 6400 6401 return p && p[0] == 0x0a && p[1] == '\033' ? ++p : NULL; 6402 } 6403 6404 /* 6405 * mch_write(): write the output buffer to the screen, translating ESC 6406 * sequences into calls to console output routines. 6407 */ 6408 void 6409 mch_write( 6410 char_u *s, 6411 int len) 6412 { 6413 char_u *end = s + len; 6414 6415 # ifdef VIMDLL 6416 if (gui.in_use) 6417 return; 6418 # endif 6419 6420 if (!term_console) 6421 { 6422 write(1, s, (unsigned)len); 6423 return; 6424 } 6425 6426 // translate ESC | sequences into faked bios calls 6427 while (len--) 6428 { 6429 int prefix = -1; 6430 char_u ch; 6431 6432 // While processing a sequence, on rare occasions it seems that another 6433 // sequence may be inserted asynchronously. 6434 if (len < 0) 6435 { 6436 redraw_all_later(CLEAR); 6437 return; 6438 } 6439 6440 while (s + ++prefix < end) 6441 { 6442 ch = s[prefix]; 6443 if (ch <= 0x1e && !(ch != '\n' && ch != '\r' && ch != '\b' 6444 && ch != '\a' && ch != '\033')) 6445 break; 6446 } 6447 6448 if (p_wd) 6449 { 6450 WaitForChar(p_wd, FALSE); 6451 if (prefix != 0) 6452 prefix = 1; 6453 } 6454 6455 if (prefix != 0) 6456 { 6457 DWORD nWritten; 6458 6459 nWritten = write_chars(s, prefix); 6460 # ifdef MCH_WRITE_DUMP 6461 if (fdDump) 6462 { 6463 fputc('>', fdDump); 6464 fwrite(s, sizeof(char_u), nWritten, fdDump); 6465 fputs("<\n", fdDump); 6466 } 6467 # endif 6468 len -= (nWritten - 1); 6469 s += nWritten; 6470 } 6471 else if (s[0] == '\n') 6472 { 6473 // \n, newline: go to the beginning of the next line or scroll 6474 if (g_coord.Y == g_srScrollRegion.Bottom) 6475 { 6476 scroll(1); 6477 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1); 6478 } 6479 else 6480 { 6481 gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2); 6482 } 6483 # ifdef MCH_WRITE_DUMP 6484 if (fdDump) 6485 fputs("\\n\n", fdDump); 6486 # endif 6487 s++; 6488 } 6489 else if (s[0] == '\r') 6490 { 6491 // \r, carriage return: go to beginning of line 6492 gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1); 6493 # ifdef MCH_WRITE_DUMP 6494 if (fdDump) 6495 fputs("\\r\n", fdDump); 6496 # endif 6497 s++; 6498 } 6499 else if (s[0] == '\b') 6500 { 6501 // \b, backspace: move cursor one position left 6502 if (g_coord.X > g_srScrollRegion.Left) 6503 g_coord.X--; 6504 else if (g_coord.Y > g_srScrollRegion.Top) 6505 { 6506 g_coord.X = g_srScrollRegion.Right; 6507 g_coord.Y--; 6508 } 6509 gotoxy(g_coord.X + 1, g_coord.Y + 1); 6510 # ifdef MCH_WRITE_DUMP 6511 if (fdDump) 6512 fputs("\\b\n", fdDump); 6513 # endif 6514 s++; 6515 } 6516 else if (s[0] == '\a') 6517 { 6518 // \a, bell 6519 MessageBeep(0xFFFFFFFF); 6520 # ifdef MCH_WRITE_DUMP 6521 if (fdDump) 6522 fputs("\\a\n", fdDump); 6523 # endif 6524 s++; 6525 } 6526 else if (s[0] == ESC && len >= 3-1 && s[1] == '|') 6527 { 6528 # ifdef MCH_WRITE_DUMP 6529 char_u *old_s = s; 6530 # endif 6531 char_u *p; 6532 int arg1 = 0, arg2 = 0, argc = 0, args[16]; 6533 char_u *sp; 6534 6535 switch (s[2]) 6536 { 6537 case '0': case '1': case '2': case '3': case '4': 6538 case '5': case '6': case '7': case '8': case '9': 6539 if (*(p = get_seq(args, &argc, s)) != 'm') 6540 goto notsgr; 6541 6542 p = s; 6543 6544 // Handling frequent optional sequences. Output to the screen 6545 // takes too long, so do not output as much as possible. 6546 6547 // If resetFG,FG,BG,<cr>,BG,FG are connected, the preceding 6548 // resetFG,FG,BG are omitted. 6549 if (sgrn2(sgrn2(sgrn2cn(sgrn2(sgrnc(p, 39), 38), 48), 48), 38)) 6550 { 6551 p = sgrn2(sgrn2(sgrnc(p, 39), 38), 48); 6552 len = len + 1 - (int)(p - s); 6553 s = p; 6554 break; 6555 } 6556 6557 // If FG,BG,BG,FG of SGR are connected, the first FG can be 6558 // omitted. 6559 if (sgrn2(sgrn2(sgrn2c((sp = sgrn2(p, 38)), 48), 48), 38)) 6560 p = sp; 6561 6562 // If FG,BG,FG,BG of SGR are connected, the first FG can be 6563 // omitted. 6564 if (sgrn2(sgrn2(sgrn2c((sp = sgrn2(p, 38)), 48), 38), 48)) 6565 p = sp; 6566 6567 // If BG,BG of SGR are connected, the first BG can be omitted. 6568 if (sgrn2((sp = sgrn2(p, 48)), 48)) 6569 p = sp; 6570 6571 // If restoreFG and FG are connected, the restoreFG can be 6572 // omitted. 6573 if (sgrn2((sp = sgrnc(p, 39)), 38)) 6574 p = sp; 6575 6576 p = get_seq(args, &argc, p); 6577 6578 notsgr: 6579 arg1 = args[0]; 6580 arg2 = args[1]; 6581 if (*p == 'm') 6582 { 6583 if (argc == 1 && args[0] == 0) 6584 normvideo(); 6585 else if (argc == 1) 6586 { 6587 if (USE_VTP) 6588 textcolor((WORD) arg1); 6589 else 6590 textattr((WORD) arg1); 6591 } 6592 else if (USE_VTP) 6593 vtp_sgr_bulks(argc, args); 6594 } 6595 else if (argc == 2 && *p == 'H') 6596 { 6597 gotoxy(arg2, arg1); 6598 } 6599 else if (argc == 2 && *p == 'r') 6600 { 6601 set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1); 6602 } 6603 else if (argc == 2 && *p == 'R') 6604 { 6605 set_scroll_region_tb(arg1, arg2); 6606 } 6607 else if (argc == 2 && *p == 'V') 6608 { 6609 set_scroll_region_lr(arg1, arg2); 6610 } 6611 else if (argc == 1 && *p == 'A') 6612 { 6613 gotoxy(g_coord.X + 1, 6614 max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1); 6615 } 6616 else if (argc == 1 && *p == 'b') 6617 { 6618 textbackground((WORD) arg1); 6619 } 6620 else if (argc == 1 && *p == 'C') 6621 { 6622 gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1, 6623 g_coord.Y + 1); 6624 } 6625 else if (argc == 1 && *p == 'f') 6626 { 6627 textcolor((WORD) arg1); 6628 } 6629 else if (argc == 1 && *p == 'H') 6630 { 6631 gotoxy(1, arg1); 6632 } 6633 else if (argc == 1 && *p == 'L') 6634 { 6635 insert_lines(arg1); 6636 } 6637 else if (argc == 1 && *p == 'M') 6638 { 6639 delete_lines(arg1); 6640 } 6641 6642 len -= (int)(p - s); 6643 s = p + 1; 6644 break; 6645 6646 case 'A': 6647 gotoxy(g_coord.X + 1, 6648 max(g_srScrollRegion.Top, g_coord.Y - 1) + 1); 6649 goto got3; 6650 6651 case 'B': 6652 visual_bell(); 6653 goto got3; 6654 6655 case 'C': 6656 gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1, 6657 g_coord.Y + 1); 6658 goto got3; 6659 6660 case 'E': 6661 termcap_mode_end(); 6662 goto got3; 6663 6664 case 'F': 6665 standout(); 6666 goto got3; 6667 6668 case 'f': 6669 standend(); 6670 goto got3; 6671 6672 case 'H': 6673 gotoxy(1, 1); 6674 goto got3; 6675 6676 case 'j': 6677 clear_to_end_of_display(); 6678 goto got3; 6679 6680 case 'J': 6681 clear_screen(); 6682 goto got3; 6683 6684 case 'K': 6685 clear_to_end_of_line(); 6686 goto got3; 6687 6688 case 'L': 6689 insert_lines(1); 6690 goto got3; 6691 6692 case 'M': 6693 delete_lines(1); 6694 goto got3; 6695 6696 case 'S': 6697 termcap_mode_start(); 6698 goto got3; 6699 6700 case 'V': 6701 cursor_visible(TRUE); 6702 goto got3; 6703 6704 case 'v': 6705 cursor_visible(FALSE); 6706 goto got3; 6707 6708 got3: 6709 s += 3; 6710 len -= 2; 6711 } 6712 6713 # ifdef MCH_WRITE_DUMP 6714 if (fdDump) 6715 { 6716 fputs("ESC | ", fdDump); 6717 fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump); 6718 fputc('\n', fdDump); 6719 } 6720 # endif 6721 } 6722 else 6723 { 6724 // Write a single character 6725 DWORD nWritten; 6726 6727 nWritten = write_chars(s, 1); 6728 # ifdef MCH_WRITE_DUMP 6729 if (fdDump) 6730 { 6731 fputc('>', fdDump); 6732 fwrite(s, sizeof(char_u), nWritten, fdDump); 6733 fputs("<\n", fdDump); 6734 } 6735 # endif 6736 6737 len -= (nWritten - 1); 6738 s += nWritten; 6739 } 6740 } 6741 6742 # ifdef MCH_WRITE_DUMP 6743 if (fdDump) 6744 fflush(fdDump); 6745 # endif 6746 } 6747 6748 #endif // FEAT_GUI_MSWIN 6749 6750 6751 /* 6752 * Delay for "msec" milliseconds. 6753 */ 6754 void 6755 mch_delay( 6756 long msec, 6757 int flags UNUSED) 6758 { 6759 #if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL) 6760 Sleep((int)msec); // never wait for input 6761 #else // Console 6762 # ifdef VIMDLL 6763 if (gui.in_use) 6764 { 6765 Sleep((int)msec); // never wait for input 6766 return; 6767 } 6768 # endif 6769 if (flags & MCH_DELAY_IGNOREINPUT) 6770 # ifdef FEAT_MZSCHEME 6771 if (mzthreads_allowed() && p_mzq > 0 && msec > p_mzq) 6772 { 6773 int towait = p_mzq; 6774 6775 // if msec is large enough, wait by portions in p_mzq 6776 while (msec > 0) 6777 { 6778 mzvim_check_threads(); 6779 if (msec < towait) 6780 towait = msec; 6781 Sleep(towait); 6782 msec -= towait; 6783 } 6784 } 6785 else 6786 # endif 6787 Sleep((int)msec); 6788 else 6789 WaitForChar(msec, FALSE); 6790 #endif 6791 } 6792 6793 6794 /* 6795 * This version of remove is not scared by a readonly (backup) file. 6796 * This can also remove a symbolic link like Unix. 6797 * Return 0 for success, -1 for failure. 6798 */ 6799 int 6800 mch_remove(char_u *name) 6801 { 6802 WCHAR *wn; 6803 int n; 6804 6805 /* 6806 * On Windows, deleting a directory's symbolic link is done by 6807 * RemoveDirectory(): mch_rmdir. It seems unnatural, but it is fact. 6808 */ 6809 if (mch_isdir(name) && mch_is_symbolic_link(name)) 6810 return mch_rmdir(name); 6811 6812 win32_setattrs(name, FILE_ATTRIBUTE_NORMAL); 6813 6814 wn = enc_to_utf16(name, NULL); 6815 if (wn == NULL) 6816 return -1; 6817 6818 n = DeleteFileW(wn) ? 0 : -1; 6819 vim_free(wn); 6820 return n; 6821 } 6822 6823 6824 /* 6825 * Check for an "interrupt signal": CTRL-break or CTRL-C. 6826 */ 6827 void 6828 mch_breakcheck(int force UNUSED) 6829 { 6830 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 6831 # ifdef VIMDLL 6832 if (!gui.in_use) 6833 # endif 6834 if (g_fCtrlCPressed || g_fCBrkPressed) 6835 { 6836 ctrl_break_was_pressed = g_fCBrkPressed; 6837 g_fCtrlCPressed = g_fCBrkPressed = FALSE; 6838 got_int = TRUE; 6839 } 6840 #endif 6841 } 6842 6843 // physical RAM to leave for the OS 6844 #define WINNT_RESERVE_BYTES (256*1024*1024) 6845 6846 /* 6847 * How much main memory in KiB that can be used by VIM. 6848 */ 6849 long_u 6850 mch_total_mem(int special UNUSED) 6851 { 6852 MEMORYSTATUSEX ms; 6853 6854 // Need to use GlobalMemoryStatusEx() when there is more memory than 6855 // what fits in 32 bits. 6856 ms.dwLength = sizeof(MEMORYSTATUSEX); 6857 GlobalMemoryStatusEx(&ms); 6858 if (ms.ullAvailVirtual < ms.ullTotalPhys) 6859 { 6860 // Process address space fits in physical RAM, use all of it. 6861 return (long_u)(ms.ullAvailVirtual / 1024); 6862 } 6863 if (ms.ullTotalPhys <= WINNT_RESERVE_BYTES) 6864 { 6865 // Catch old NT box or perverse hardware setup. 6866 return (long_u)((ms.ullTotalPhys / 2) / 1024); 6867 } 6868 // Use physical RAM less reserve for OS + data. 6869 return (long_u)((ms.ullTotalPhys - WINNT_RESERVE_BYTES) / 1024); 6870 } 6871 6872 /* 6873 * mch_wrename() works around a bug in rename (aka MoveFile) in 6874 * Windows 95: rename("foo.bar", "foo.bar~") will generate a 6875 * file whose short file name is "FOO.BAR" (its long file name will 6876 * be correct: "foo.bar~"). Because a file can be accessed by 6877 * either its SFN or its LFN, "foo.bar" has effectively been 6878 * renamed to "foo.bar", which is not at all what was wanted. This 6879 * seems to happen only when renaming files with three-character 6880 * extensions by appending a suffix that does not include ".". 6881 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR". 6882 * 6883 * There is another problem, which isn't really a bug but isn't right either: 6884 * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be 6885 * "abcdef~1.txt" again. This has been reported on Windows NT 4.0 with 6886 * service pack 6. Doesn't seem to happen on Windows 98. 6887 * 6888 * Like rename(), returns 0 upon success, non-zero upon failure. 6889 * Should probably set errno appropriately when errors occur. 6890 */ 6891 int 6892 mch_wrename(WCHAR *wold, WCHAR *wnew) 6893 { 6894 WCHAR *p; 6895 int i; 6896 WCHAR szTempFile[_MAX_PATH + 1]; 6897 WCHAR szNewPath[_MAX_PATH + 1]; 6898 HANDLE hf; 6899 6900 // No need to play tricks unless the file name contains a "~" as the 6901 // seventh character. 6902 p = wold; 6903 for (i = 0; wold[i] != NUL; ++i) 6904 if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':') 6905 && wold[i + 1] != 0) 6906 p = wold + i + 1; 6907 if ((int)(wold + i - p) < 8 || p[6] != '~') 6908 return (MoveFileW(wold, wnew) == 0); 6909 6910 // Get base path of new file name. Undocumented feature: If pszNewFile is 6911 // a directory, no error is returned and pszFilePart will be NULL. 6912 if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL) 6913 return -1; 6914 *p = NUL; 6915 6916 // Get (and create) a unique temporary file name in directory of new file 6917 if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0) 6918 return -2; 6919 6920 // blow the temp file away 6921 if (!DeleteFileW(szTempFile)) 6922 return -3; 6923 6924 // rename old file to the temp file 6925 if (!MoveFileW(wold, szTempFile)) 6926 return -4; 6927 6928 // now create an empty file called pszOldFile; this prevents the operating 6929 // system using pszOldFile as an alias (SFN) if we're renaming within the 6930 // same directory. For example, we're editing a file called 6931 // filename.asc.txt by its SFN, filena~1.txt. If we rename filena~1.txt 6932 // to filena~1.txt~ (i.e., we're making a backup while writing it), the 6933 // SFN for filena~1.txt~ will be filena~1.txt, by default, which will 6934 // cause all sorts of problems later in buf_write(). So, we create an 6935 // empty file called filena~1.txt and the system will have to find some 6936 // other SFN for filena~1.txt~, such as filena~2.txt 6937 if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW, 6938 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) 6939 return -5; 6940 if (!CloseHandle(hf)) 6941 return -6; 6942 6943 // rename the temp file to the new file 6944 if (!MoveFileW(szTempFile, wnew)) 6945 { 6946 // Renaming failed. Rename the file back to its old name, so that it 6947 // looks like nothing happened. 6948 (void)MoveFileW(szTempFile, wold); 6949 return -7; 6950 } 6951 6952 // Seems to be left around on Novell filesystems 6953 DeleteFileW(szTempFile); 6954 6955 // finally, remove the empty old file 6956 if (!DeleteFileW(wold)) 6957 return -8; 6958 6959 return 0; 6960 } 6961 6962 6963 /* 6964 * Converts the filenames to UTF-16, then call mch_wrename(). 6965 * Like rename(), returns 0 upon success, non-zero upon failure. 6966 */ 6967 int 6968 mch_rename( 6969 const char *pszOldFile, 6970 const char *pszNewFile) 6971 { 6972 WCHAR *wold = NULL; 6973 WCHAR *wnew = NULL; 6974 int retval = -1; 6975 6976 wold = enc_to_utf16((char_u *)pszOldFile, NULL); 6977 wnew = enc_to_utf16((char_u *)pszNewFile, NULL); 6978 if (wold != NULL && wnew != NULL) 6979 retval = mch_wrename(wold, wnew); 6980 vim_free(wold); 6981 vim_free(wnew); 6982 return retval; 6983 } 6984 6985 /* 6986 * Get the default shell for the current hardware platform 6987 */ 6988 char * 6989 default_shell(void) 6990 { 6991 return "cmd.exe"; 6992 } 6993 6994 /* 6995 * mch_access() extends access() to do more detailed check on network drives. 6996 * Returns 0 if file "n" has access rights according to "p", -1 otherwise. 6997 */ 6998 int 6999 mch_access(char *n, int p) 7000 { 7001 HANDLE hFile; 7002 int retval = -1; // default: fail 7003 WCHAR *wn; 7004 7005 wn = enc_to_utf16((char_u *)n, NULL); 7006 if (wn == NULL) 7007 return -1; 7008 7009 if (mch_isdir((char_u *)n)) 7010 { 7011 WCHAR TempNameW[_MAX_PATH + 16] = L""; 7012 7013 if (p & R_OK) 7014 { 7015 // Read check is performed by seeing if we can do a find file on 7016 // the directory for any file. 7017 int i; 7018 WIN32_FIND_DATAW d; 7019 7020 for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i) 7021 TempNameW[i] = wn[i]; 7022 if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/') 7023 TempNameW[i++] = '\\'; 7024 TempNameW[i++] = '*'; 7025 TempNameW[i++] = 0; 7026 7027 hFile = FindFirstFileW(TempNameW, &d); 7028 if (hFile == INVALID_HANDLE_VALUE) 7029 goto getout; 7030 else 7031 (void)FindClose(hFile); 7032 } 7033 7034 if (p & W_OK) 7035 { 7036 // Trying to create a temporary file in the directory should catch 7037 // directories on read-only network shares. However, in 7038 // directories whose ACL allows writes but denies deletes will end 7039 // up keeping the temporary file :-(. 7040 if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW)) 7041 goto getout; 7042 else 7043 DeleteFileW(TempNameW); 7044 } 7045 } 7046 else 7047 { 7048 // Don't consider a file read-only if another process has opened it. 7049 DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; 7050 7051 // Trying to open the file for the required access does ACL, read-only 7052 // network share, and file attribute checks. 7053 DWORD access_mode = ((p & W_OK) ? GENERIC_WRITE : 0) 7054 | ((p & R_OK) ? GENERIC_READ : 0); 7055 7056 hFile = CreateFileW(wn, access_mode, share_mode, 7057 NULL, OPEN_EXISTING, 0, NULL); 7058 if (hFile == INVALID_HANDLE_VALUE) 7059 goto getout; 7060 CloseHandle(hFile); 7061 } 7062 7063 retval = 0; // success 7064 getout: 7065 vim_free(wn); 7066 return retval; 7067 } 7068 7069 /* 7070 * Version of open() that may use UTF-16 file name. 7071 */ 7072 int 7073 mch_open(const char *name, int flags, int mode) 7074 { 7075 WCHAR *wn; 7076 int f; 7077 7078 wn = enc_to_utf16((char_u *)name, NULL); 7079 if (wn == NULL) 7080 return -1; 7081 7082 f = _wopen(wn, flags, mode); 7083 vim_free(wn); 7084 return f; 7085 } 7086 7087 /* 7088 * Version of fopen() that uses UTF-16 file name. 7089 */ 7090 FILE * 7091 mch_fopen(const char *name, const char *mode) 7092 { 7093 WCHAR *wn, *wm; 7094 FILE *f = NULL; 7095 7096 #if defined(DEBUG) && _MSC_VER >= 1400 7097 // Work around an annoying assertion in the Microsoft debug CRT 7098 // when mode's text/binary setting doesn't match _get_fmode(). 7099 char newMode = mode[strlen(mode) - 1]; 7100 int oldMode = 0; 7101 7102 _get_fmode(&oldMode); 7103 if (newMode == 't') 7104 _set_fmode(_O_TEXT); 7105 else if (newMode == 'b') 7106 _set_fmode(_O_BINARY); 7107 #endif 7108 wn = enc_to_utf16((char_u *)name, NULL); 7109 wm = enc_to_utf16((char_u *)mode, NULL); 7110 if (wn != NULL && wm != NULL) 7111 f = _wfopen(wn, wm); 7112 vim_free(wn); 7113 vim_free(wm); 7114 7115 #if defined(DEBUG) && _MSC_VER >= 1400 7116 _set_fmode(oldMode); 7117 #endif 7118 return f; 7119 } 7120 7121 /* 7122 * SUB STREAM (aka info stream) handling: 7123 * 7124 * NTFS can have sub streams for each file. The normal contents of a file is 7125 * stored in the main stream, and extra contents (author information, title and 7126 * so on) can be stored in a sub stream. After Windows 2000, the user can 7127 * access and store this information in sub streams via an explorer's property 7128 * menu item in the right click menu. This information in sub streams was lost 7129 * when copying only the main stream. Therefore we have to copy sub streams. 7130 * 7131 * Incomplete explanation: 7132 * http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp 7133 * More useful info and an example: 7134 * http://www.sysinternals.com/ntw2k/source/misc.shtml#streams 7135 */ 7136 7137 /* 7138 * Copy info stream data "substream". Read from the file with BackupRead(sh) 7139 * and write to stream "substream" of file "to". 7140 * Errors are ignored. 7141 */ 7142 static void 7143 copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len) 7144 { 7145 HANDLE hTo; 7146 WCHAR *to_name; 7147 7148 to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR)); 7149 wcscpy(to_name, to); 7150 wcscat(to_name, substream); 7151 7152 hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 7153 FILE_ATTRIBUTE_NORMAL, NULL); 7154 if (hTo != INVALID_HANDLE_VALUE) 7155 { 7156 long done; 7157 DWORD todo; 7158 DWORD readcnt, written; 7159 char buf[4096]; 7160 7161 // Copy block of bytes at a time. Abort when something goes wrong. 7162 for (done = 0; done < len; done += written) 7163 { 7164 // (size_t) cast for Borland C 5.5 7165 todo = (DWORD)((size_t)(len - done) > sizeof(buf) ? sizeof(buf) 7166 : (size_t)(len - done)); 7167 if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt, 7168 FALSE, FALSE, context) 7169 || readcnt != todo 7170 || !WriteFile(hTo, buf, todo, &written, NULL) 7171 || written != todo) 7172 break; 7173 } 7174 CloseHandle(hTo); 7175 } 7176 7177 free(to_name); 7178 } 7179 7180 /* 7181 * Copy info streams from file "from" to file "to". 7182 */ 7183 static void 7184 copy_infostreams(char_u *from, char_u *to) 7185 { 7186 WCHAR *fromw; 7187 WCHAR *tow; 7188 HANDLE sh; 7189 WIN32_STREAM_ID sid; 7190 int headersize; 7191 WCHAR streamname[_MAX_PATH]; 7192 DWORD readcount; 7193 void *context = NULL; 7194 DWORD lo, hi; 7195 int len; 7196 7197 // Convert the file names to wide characters. 7198 fromw = enc_to_utf16(from, NULL); 7199 tow = enc_to_utf16(to, NULL); 7200 if (fromw != NULL && tow != NULL) 7201 { 7202 // Open the file for reading. 7203 sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL, 7204 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 7205 if (sh != INVALID_HANDLE_VALUE) 7206 { 7207 // Use BackupRead() to find the info streams. Repeat until we 7208 // have done them all. 7209 for (;;) 7210 { 7211 // Get the header to find the length of the stream name. If 7212 // the "readcount" is zero we have done all info streams. 7213 ZeroMemory(&sid, sizeof(WIN32_STREAM_ID)); 7214 headersize = (int)((char *)&sid.cStreamName - (char *)&sid.dwStreamId); 7215 if (!BackupRead(sh, (LPBYTE)&sid, headersize, 7216 &readcount, FALSE, FALSE, &context) 7217 || readcount == 0) 7218 break; 7219 7220 // We only deal with streams that have a name. The normal 7221 // file data appears to be without a name, even though docs 7222 // suggest it is called "::$DATA". 7223 if (sid.dwStreamNameSize > 0) 7224 { 7225 // Read the stream name. 7226 if (!BackupRead(sh, (LPBYTE)streamname, 7227 sid.dwStreamNameSize, 7228 &readcount, FALSE, FALSE, &context)) 7229 break; 7230 7231 // Copy an info stream with a name ":anything:$DATA". 7232 // Skip "::$DATA", it has no stream name (examples suggest 7233 // it might be used for the normal file contents). 7234 // Note that BackupRead() counts bytes, but the name is in 7235 // wide characters. 7236 len = readcount / sizeof(WCHAR); 7237 streamname[len] = 0; 7238 if (len > 7 && wcsicmp(streamname + len - 6, 7239 L":$DATA") == 0) 7240 { 7241 streamname[len - 6] = 0; 7242 copy_substream(sh, &context, tow, streamname, 7243 (long)sid.Size.u.LowPart); 7244 } 7245 } 7246 7247 // Advance to the next stream. We might try seeking too far, 7248 // but BackupSeek() doesn't skip over stream borders, thus 7249 // that's OK. 7250 (void)BackupSeek(sh, sid.Size.u.LowPart, sid.Size.u.HighPart, 7251 &lo, &hi, &context); 7252 } 7253 7254 // Clear the context. 7255 (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context); 7256 7257 CloseHandle(sh); 7258 } 7259 } 7260 vim_free(fromw); 7261 vim_free(tow); 7262 } 7263 7264 /* 7265 * ntdll.dll definitions 7266 */ 7267 #define FileEaInformation 7 7268 #ifndef STATUS_SUCCESS 7269 # define STATUS_SUCCESS ((NTSTATUS) 0x00000000L) 7270 #endif 7271 7272 typedef struct _FILE_FULL_EA_INFORMATION_ { 7273 ULONG NextEntryOffset; 7274 UCHAR Flags; 7275 UCHAR EaNameLength; 7276 USHORT EaValueLength; 7277 CHAR EaName[1]; 7278 } FILE_FULL_EA_INFORMATION_, *PFILE_FULL_EA_INFORMATION_; 7279 7280 typedef struct _FILE_EA_INFORMATION_ { 7281 ULONG EaSize; 7282 } FILE_EA_INFORMATION_, *PFILE_EA_INFORMATION_; 7283 7284 typedef NTSTATUS (NTAPI *PfnNtOpenFile)( 7285 PHANDLE FileHandle, 7286 ACCESS_MASK DesiredAccess, 7287 POBJECT_ATTRIBUTES ObjectAttributes, 7288 PIO_STATUS_BLOCK IoStatusBlock, 7289 ULONG ShareAccess, 7290 ULONG OpenOptions); 7291 typedef NTSTATUS (NTAPI *PfnNtClose)( 7292 HANDLE Handle); 7293 typedef NTSTATUS (NTAPI *PfnNtSetEaFile)( 7294 HANDLE FileHandle, 7295 PIO_STATUS_BLOCK IoStatusBlock, 7296 PVOID Buffer, 7297 ULONG Length); 7298 typedef NTSTATUS (NTAPI *PfnNtQueryEaFile)( 7299 HANDLE FileHandle, 7300 PIO_STATUS_BLOCK IoStatusBlock, 7301 PVOID Buffer, 7302 ULONG Length, 7303 BOOLEAN ReturnSingleEntry, 7304 PVOID EaList, 7305 ULONG EaListLength, 7306 PULONG EaIndex, 7307 BOOLEAN RestartScan); 7308 typedef NTSTATUS (NTAPI *PfnNtQueryInformationFile)( 7309 HANDLE FileHandle, 7310 PIO_STATUS_BLOCK IoStatusBlock, 7311 PVOID FileInformation, 7312 ULONG Length, 7313 FILE_INFORMATION_CLASS FileInformationClass); 7314 typedef VOID (NTAPI *PfnRtlInitUnicodeString)( 7315 PUNICODE_STRING DestinationString, 7316 PCWSTR SourceString); 7317 7318 PfnNtOpenFile pNtOpenFile = NULL; 7319 PfnNtClose pNtClose = NULL; 7320 PfnNtSetEaFile pNtSetEaFile = NULL; 7321 PfnNtQueryEaFile pNtQueryEaFile = NULL; 7322 PfnNtQueryInformationFile pNtQueryInformationFile = NULL; 7323 PfnRtlInitUnicodeString pRtlInitUnicodeString = NULL; 7324 7325 /* 7326 * Load ntdll.dll functions. 7327 */ 7328 static BOOL 7329 load_ntdll(void) 7330 { 7331 static int loaded = -1; 7332 7333 if (loaded == -1) 7334 { 7335 HMODULE hNtdll = GetModuleHandle("ntdll.dll"); 7336 if (hNtdll != NULL) 7337 { 7338 pNtOpenFile = (PfnNtOpenFile) GetProcAddress(hNtdll, "NtOpenFile"); 7339 pNtClose = (PfnNtClose) GetProcAddress(hNtdll, "NtClose"); 7340 pNtSetEaFile = (PfnNtSetEaFile) 7341 GetProcAddress(hNtdll, "NtSetEaFile"); 7342 pNtQueryEaFile = (PfnNtQueryEaFile) 7343 GetProcAddress(hNtdll, "NtQueryEaFile"); 7344 pNtQueryInformationFile = (PfnNtQueryInformationFile) 7345 GetProcAddress(hNtdll, "NtQueryInformationFile"); 7346 pRtlInitUnicodeString = (PfnRtlInitUnicodeString) 7347 GetProcAddress(hNtdll, "RtlInitUnicodeString"); 7348 } 7349 if (pNtOpenFile == NULL 7350 || pNtClose == NULL 7351 || pNtSetEaFile == NULL 7352 || pNtQueryEaFile == NULL 7353 || pNtQueryInformationFile == NULL 7354 || pRtlInitUnicodeString == NULL) 7355 loaded = FALSE; 7356 else 7357 loaded = TRUE; 7358 } 7359 return (BOOL) loaded; 7360 } 7361 7362 /* 7363 * Copy extended attributes (EA) from file "from" to file "to". 7364 */ 7365 static void 7366 copy_extattr(char_u *from, char_u *to) 7367 { 7368 char_u *fromf = NULL; 7369 char_u *tof = NULL; 7370 WCHAR *fromw = NULL; 7371 WCHAR *tow = NULL; 7372 UNICODE_STRING u; 7373 HANDLE h; 7374 OBJECT_ATTRIBUTES oa; 7375 IO_STATUS_BLOCK iosb; 7376 FILE_EA_INFORMATION_ eainfo = {0}; 7377 void *ea = NULL; 7378 7379 if (!load_ntdll()) 7380 return; 7381 7382 // Convert the file names to the fully qualified object names. 7383 fromf = alloc(STRLEN(from) + 5); 7384 tof = alloc(STRLEN(to) + 5); 7385 if (fromf == NULL || tof == NULL) 7386 goto theend; 7387 STRCPY(fromf, "\\??\\"); 7388 STRCAT(fromf, from); 7389 STRCPY(tof, "\\??\\"); 7390 STRCAT(tof, to); 7391 7392 // Convert the names to wide characters. 7393 fromw = enc_to_utf16(fromf, NULL); 7394 tow = enc_to_utf16(tof, NULL); 7395 if (fromw == NULL || tow == NULL) 7396 goto theend; 7397 7398 // Get the EA. 7399 pRtlInitUnicodeString(&u, fromw); 7400 InitializeObjectAttributes(&oa, &u, 0, NULL, NULL); 7401 if (pNtOpenFile(&h, FILE_READ_EA, &oa, &iosb, 0, 7402 FILE_NON_DIRECTORY_FILE) != STATUS_SUCCESS) 7403 goto theend; 7404 pNtQueryInformationFile(h, &iosb, &eainfo, sizeof(eainfo), 7405 FileEaInformation); 7406 if (eainfo.EaSize != 0) 7407 { 7408 ea = alloc(eainfo.EaSize); 7409 if (ea != NULL) 7410 { 7411 if (pNtQueryEaFile(h, &iosb, ea, eainfo.EaSize, FALSE, 7412 NULL, 0, NULL, TRUE) != STATUS_SUCCESS) 7413 { 7414 vim_free(ea); 7415 ea = NULL; 7416 } 7417 } 7418 } 7419 pNtClose(h); 7420 7421 // Set the EA. 7422 if (ea != NULL) 7423 { 7424 pRtlInitUnicodeString(&u, tow); 7425 InitializeObjectAttributes(&oa, &u, 0, NULL, NULL); 7426 if (pNtOpenFile(&h, FILE_WRITE_EA, &oa, &iosb, 0, 7427 FILE_NON_DIRECTORY_FILE) != STATUS_SUCCESS) 7428 goto theend; 7429 7430 pNtSetEaFile(h, &iosb, ea, eainfo.EaSize); 7431 pNtClose(h); 7432 } 7433 7434 theend: 7435 vim_free(fromf); 7436 vim_free(tof); 7437 vim_free(fromw); 7438 vim_free(tow); 7439 vim_free(ea); 7440 } 7441 7442 /* 7443 * Copy file attributes from file "from" to file "to". 7444 * For Windows NT and later we copy info streams. 7445 * Always returns zero, errors are ignored. 7446 */ 7447 int 7448 mch_copy_file_attribute(char_u *from, char_u *to) 7449 { 7450 // File streams only work on Windows NT and later. 7451 copy_infostreams(from, to); 7452 copy_extattr(from, to); 7453 return 0; 7454 } 7455 7456 #if defined(MYRESETSTKOFLW) || defined(PROTO) 7457 /* 7458 * Recreate a destroyed stack guard page in win32. 7459 * Written by Benjamin Peterson. 7460 */ 7461 7462 // These magic numbers are from the MS header files 7463 # define MIN_STACK_WINNT 2 7464 7465 /* 7466 * This function does the same thing as _resetstkoflw(), which is only 7467 * available in DevStudio .net and later. 7468 * Returns 0 for failure, 1 for success. 7469 */ 7470 int 7471 myresetstkoflw(void) 7472 { 7473 BYTE *pStackPtr; 7474 BYTE *pGuardPage; 7475 BYTE *pStackBase; 7476 BYTE *pLowestPossiblePage; 7477 MEMORY_BASIC_INFORMATION mbi; 7478 SYSTEM_INFO si; 7479 DWORD nPageSize; 7480 DWORD dummy; 7481 7482 // We need to know the system page size. 7483 GetSystemInfo(&si); 7484 nPageSize = si.dwPageSize; 7485 7486 // ...and the current stack pointer 7487 pStackPtr = (BYTE*)_alloca(1); 7488 7489 // ...and the base of the stack. 7490 if (VirtualQuery(pStackPtr, &mbi, sizeof mbi) == 0) 7491 return 0; 7492 pStackBase = (BYTE*)mbi.AllocationBase; 7493 7494 // ...and the page that's min_stack_req pages away from stack base; this is 7495 // the lowest page we could use. 7496 pLowestPossiblePage = pStackBase + MIN_STACK_WINNT * nPageSize; 7497 7498 { 7499 // We want the first committed page in the stack Start at the stack 7500 // base and move forward through memory until we find a committed block. 7501 BYTE *pBlock = pStackBase; 7502 7503 for (;;) 7504 { 7505 if (VirtualQuery(pBlock, &mbi, sizeof mbi) == 0) 7506 return 0; 7507 7508 pBlock += mbi.RegionSize; 7509 7510 if (mbi.State & MEM_COMMIT) 7511 break; 7512 } 7513 7514 // mbi now describes the first committed block in the stack. 7515 if (mbi.Protect & PAGE_GUARD) 7516 return 1; 7517 7518 // decide where the guard page should start 7519 if ((long_u)(mbi.BaseAddress) < (long_u)pLowestPossiblePage) 7520 pGuardPage = pLowestPossiblePage; 7521 else 7522 pGuardPage = (BYTE*)mbi.BaseAddress; 7523 7524 // allocate the guard page 7525 if (!VirtualAlloc(pGuardPage, nPageSize, MEM_COMMIT, PAGE_READWRITE)) 7526 return 0; 7527 7528 // apply the guard attribute to the page 7529 if (!VirtualProtect(pGuardPage, nPageSize, PAGE_READWRITE | PAGE_GUARD, 7530 &dummy)) 7531 return 0; 7532 } 7533 7534 return 1; 7535 } 7536 #endif 7537 7538 7539 /* 7540 * The command line arguments in UCS2 7541 */ 7542 static int nArgsW = 0; 7543 static LPWSTR *ArglistW = NULL; 7544 static int global_argc = 0; 7545 static char **global_argv; 7546 7547 static int used_file_argc = 0; // last argument in global_argv[] used 7548 // for the argument list. 7549 static int *used_file_indexes = NULL; // indexes in global_argv[] for 7550 // command line arguments added to 7551 // the argument list 7552 static int used_file_count = 0; // nr of entries in used_file_indexes 7553 static int used_file_literal = FALSE; // take file names literally 7554 static int used_file_full_path = FALSE; // file name was full path 7555 static int used_file_diff_mode = FALSE; // file name was with diff mode 7556 static int used_alist_count = 0; 7557 7558 7559 /* 7560 * Get the command line arguments. Unicode version. 7561 * Returns argc. Zero when something fails. 7562 */ 7563 int 7564 get_cmd_argsW(char ***argvp) 7565 { 7566 char **argv = NULL; 7567 int argc = 0; 7568 int i; 7569 7570 free_cmd_argsW(); 7571 ArglistW = CommandLineToArgvW(GetCommandLineW(), &nArgsW); 7572 if (ArglistW != NULL) 7573 { 7574 argv = malloc((nArgsW + 1) * sizeof(char *)); 7575 if (argv != NULL) 7576 { 7577 argc = nArgsW; 7578 argv[argc] = NULL; 7579 for (i = 0; i < argc; ++i) 7580 { 7581 int len; 7582 7583 // Convert each Unicode argument to the current codepage. 7584 WideCharToMultiByte_alloc(GetACP(), 0, 7585 ArglistW[i], (int)wcslen(ArglistW[i]) + 1, 7586 (LPSTR *)&argv[i], &len, 0, 0); 7587 if (argv[i] == NULL) 7588 { 7589 // Out of memory, clear everything. 7590 while (i > 0) 7591 free(argv[--i]); 7592 free(argv); 7593 argv = NULL; 7594 argc = 0; 7595 } 7596 } 7597 } 7598 } 7599 7600 global_argc = argc; 7601 global_argv = argv; 7602 if (argc > 0) 7603 { 7604 if (used_file_indexes != NULL) 7605 free(used_file_indexes); 7606 used_file_indexes = malloc(argc * sizeof(int)); 7607 } 7608 7609 if (argvp != NULL) 7610 *argvp = argv; 7611 return argc; 7612 } 7613 7614 void 7615 free_cmd_argsW(void) 7616 { 7617 if (ArglistW != NULL) 7618 { 7619 GlobalFree(ArglistW); 7620 ArglistW = NULL; 7621 } 7622 } 7623 7624 /* 7625 * Remember "name" is an argument that was added to the argument list. 7626 * This avoids that we have to re-parse the argument list when fix_arg_enc() 7627 * is called. 7628 */ 7629 void 7630 used_file_arg(char *name, int literal, int full_path, int diff_mode) 7631 { 7632 int i; 7633 7634 if (used_file_indexes == NULL) 7635 return; 7636 for (i = used_file_argc + 1; i < global_argc; ++i) 7637 if (STRCMP(global_argv[i], name) == 0) 7638 { 7639 used_file_argc = i; 7640 used_file_indexes[used_file_count++] = i; 7641 break; 7642 } 7643 used_file_literal = literal; 7644 used_file_full_path = full_path; 7645 used_file_diff_mode = diff_mode; 7646 } 7647 7648 /* 7649 * Remember the length of the argument list as it was. If it changes then we 7650 * leave it alone when 'encoding' is set. 7651 */ 7652 void 7653 set_alist_count(void) 7654 { 7655 used_alist_count = GARGCOUNT; 7656 } 7657 7658 /* 7659 * Fix the encoding of the command line arguments. Invoked when 'encoding' 7660 * has been changed while starting up. Use the UCS-2 command line arguments 7661 * and convert them to 'encoding'. 7662 */ 7663 void 7664 fix_arg_enc(void) 7665 { 7666 int i; 7667 int idx; 7668 char_u *str; 7669 int *fnum_list; 7670 7671 // Safety checks: 7672 // - if argument count differs between the wide and non-wide argument 7673 // list, something must be wrong. 7674 // - the file name arguments must have been located. 7675 // - the length of the argument list wasn't changed by the user. 7676 if (global_argc != nArgsW 7677 || ArglistW == NULL 7678 || used_file_indexes == NULL 7679 || used_file_count == 0 7680 || used_alist_count != GARGCOUNT) 7681 return; 7682 7683 // Remember the buffer numbers for the arguments. 7684 fnum_list = ALLOC_MULT(int, GARGCOUNT); 7685 if (fnum_list == NULL) 7686 return; // out of memory 7687 for (i = 0; i < GARGCOUNT; ++i) 7688 fnum_list[i] = GARGLIST[i].ae_fnum; 7689 7690 // Clear the argument list. Make room for the new arguments. 7691 alist_clear(&global_alist); 7692 if (ga_grow(&global_alist.al_ga, used_file_count) == FAIL) 7693 return; // out of memory 7694 7695 for (i = 0; i < used_file_count; ++i) 7696 { 7697 idx = used_file_indexes[i]; 7698 str = utf16_to_enc(ArglistW[idx], NULL); 7699 if (str != NULL) 7700 { 7701 int literal = used_file_literal; 7702 7703 #ifdef FEAT_DIFF 7704 // When using diff mode may need to concatenate file name to 7705 // directory name. Just like it's done in main(). 7706 if (used_file_diff_mode && mch_isdir(str) && GARGCOUNT > 0 7707 && !mch_isdir(alist_name(&GARGLIST[0]))) 7708 { 7709 char_u *r; 7710 7711 r = concat_fnames(str, gettail(alist_name(&GARGLIST[0])), TRUE); 7712 if (r != NULL) 7713 { 7714 vim_free(str); 7715 str = r; 7716 } 7717 } 7718 #endif 7719 // Re-use the old buffer by renaming it. When not using literal 7720 // names it's done by alist_expand() below. 7721 if (used_file_literal) 7722 buf_set_name(fnum_list[i], str); 7723 7724 // Check backtick literal. backtick literal is already expanded in 7725 // main.c, so this part add str as literal. 7726 if (literal == FALSE) 7727 { 7728 size_t len = STRLEN(str); 7729 7730 if (len > 2 && *str == '`' && *(str + len - 1) == '`') 7731 literal = TRUE; 7732 } 7733 alist_add(&global_alist, str, literal ? 2 : 0); 7734 } 7735 } 7736 7737 if (!used_file_literal) 7738 { 7739 // Now expand wildcards in the arguments. 7740 // Temporarily add '(' and ')' to 'isfname'. These are valid 7741 // filename characters but are excluded from 'isfname' to make 7742 // "gf" work on a file name in parenthesis (e.g.: see vim.h). 7743 // Also, unset wildignore to not be influenced by this option. 7744 // The arguments specified in command-line should be kept even if 7745 // encoding options were changed. 7746 do_cmdline_cmd((char_u *)":let SaVe_ISF = &isf|set isf+=(,)"); 7747 do_cmdline_cmd((char_u *)":let SaVe_WIG = &wig|set wig="); 7748 alist_expand(fnum_list, used_alist_count); 7749 do_cmdline_cmd((char_u *)":let &isf = SaVe_ISF|unlet SaVe_ISF"); 7750 do_cmdline_cmd((char_u *)":let &wig = SaVe_WIG|unlet SaVe_WIG"); 7751 } 7752 7753 // If wildcard expansion failed, we are editing the first file of the 7754 // arglist and there is no file name: Edit the first argument now. 7755 if (curwin->w_arg_idx == 0 && curbuf->b_fname == NULL) 7756 { 7757 do_cmdline_cmd((char_u *)":rewind"); 7758 if (GARGCOUNT == 1 && used_file_full_path) 7759 (void)vim_chdirfile(alist_name(&GARGLIST[0]), "drop"); 7760 } 7761 7762 set_alist_count(); 7763 } 7764 7765 int 7766 mch_setenv(char *var, char *value, int x UNUSED) 7767 { 7768 char_u *envbuf; 7769 WCHAR *p; 7770 7771 envbuf = alloc(STRLEN(var) + STRLEN(value) + 2); 7772 if (envbuf == NULL) 7773 return -1; 7774 7775 sprintf((char *)envbuf, "%s=%s", var, value); 7776 7777 p = enc_to_utf16(envbuf, NULL); 7778 7779 vim_free(envbuf); 7780 if (p == NULL) 7781 return -1; 7782 _wputenv(p); 7783 #ifdef libintl_wputenv 7784 libintl_wputenv(p); 7785 #endif 7786 // Unlike Un*x systems, we can free the string for _wputenv(). 7787 vim_free(p); 7788 7789 return 0; 7790 } 7791 7792 /* 7793 * Support for 256 colors and 24-bit colors was added in Windows 10 7794 * version 1703 (Creators update). 7795 */ 7796 #define VTP_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 15063) 7797 7798 /* 7799 * Support for pseudo-console (ConPTY) was added in windows 10 7800 * version 1809 (October 2018 update). 7801 */ 7802 #define CONPTY_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 17763) 7803 7804 /* 7805 * ConPTY differences between versions, need different logic. 7806 * version 1903 (May 2019 update). 7807 */ 7808 #define CONPTY_1903_BUILD MAKE_VER(10, 0, 18362) 7809 7810 /* 7811 * version 1909 (November 2019 update). 7812 */ 7813 #define CONPTY_1909_BUILD MAKE_VER(10, 0, 18363) 7814 7815 /* 7816 * Stay ahead of the next update, and when it's done, fix this. 7817 * version ? (2020 update, temporarily use the build number of insider preview) 7818 */ 7819 #define CONPTY_NEXT_UPDATE_BUILD MAKE_VER(10, 0, 19587) 7820 7821 /* 7822 * Confirm until this version. Also the logic changes. 7823 * insider preview. 7824 */ 7825 #define CONPTY_INSIDER_BUILD MAKE_VER(10, 0, 18995) 7826 7827 /* 7828 * Not stable now. 7829 */ 7830 #define CONPTY_STABLE_BUILD MAKE_VER(10, 0, 32767) // T.B.D. 7831 7832 static void 7833 vtp_flag_init(void) 7834 { 7835 DWORD ver = get_build_number(); 7836 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) 7837 DWORD mode; 7838 HANDLE out; 7839 7840 # ifdef VIMDLL 7841 if (!gui.in_use) 7842 # endif 7843 { 7844 out = GetStdHandle(STD_OUTPUT_HANDLE); 7845 7846 vtp_working = (ver >= VTP_FIRST_SUPPORT_BUILD) ? 1 : 0; 7847 GetConsoleMode(out, &mode); 7848 mode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING); 7849 if (SetConsoleMode(out, mode) == 0) 7850 vtp_working = 0; 7851 } 7852 #endif 7853 7854 if (ver >= CONPTY_FIRST_SUPPORT_BUILD) 7855 conpty_working = 1; 7856 if (ver >= CONPTY_STABLE_BUILD) 7857 conpty_stable = 1; 7858 7859 if (ver <= CONPTY_INSIDER_BUILD) 7860 conpty_type = 3; 7861 if (ver <= CONPTY_1909_BUILD) 7862 conpty_type = 2; 7863 if (ver <= CONPTY_1903_BUILD) 7864 conpty_type = 2; 7865 if (ver < CONPTY_FIRST_SUPPORT_BUILD) 7866 conpty_type = 1; 7867 7868 if (ver >= CONPTY_NEXT_UPDATE_BUILD) 7869 conpty_fix_type = 1; 7870 } 7871 7872 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) || defined(PROTO) 7873 7874 static void 7875 vtp_init(void) 7876 { 7877 HMODULE hKerneldll; 7878 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi; 7879 # ifdef FEAT_TERMGUICOLORS 7880 COLORREF fg, bg; 7881 # endif 7882 7883 // Use functions supported from Vista 7884 hKerneldll = GetModuleHandle("kernel32.dll"); 7885 if (hKerneldll != NULL) 7886 { 7887 pGetConsoleScreenBufferInfoEx = 7888 (PfnGetConsoleScreenBufferInfoEx)GetProcAddress( 7889 hKerneldll, "GetConsoleScreenBufferInfoEx"); 7890 pSetConsoleScreenBufferInfoEx = 7891 (PfnSetConsoleScreenBufferInfoEx)GetProcAddress( 7892 hKerneldll, "SetConsoleScreenBufferInfoEx"); 7893 if (pGetConsoleScreenBufferInfoEx != NULL 7894 && pSetConsoleScreenBufferInfoEx != NULL) 7895 has_csbiex = TRUE; 7896 } 7897 7898 csbi.cbSize = sizeof(csbi); 7899 if (has_csbiex) 7900 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi); 7901 save_console_bg_rgb = (guicolor_T)csbi.ColorTable[g_color_index_bg]; 7902 save_console_fg_rgb = (guicolor_T)csbi.ColorTable[g_color_index_fg]; 7903 store_console_bg_rgb = save_console_bg_rgb; 7904 store_console_fg_rgb = save_console_fg_rgb; 7905 7906 # ifdef FEAT_TERMGUICOLORS 7907 bg = (COLORREF)csbi.ColorTable[g_color_index_bg]; 7908 fg = (COLORREF)csbi.ColorTable[g_color_index_fg]; 7909 bg = (GetRValue(bg) << 16) | (GetGValue(bg) << 8) | GetBValue(bg); 7910 fg = (GetRValue(fg) << 16) | (GetGValue(fg) << 8) | GetBValue(fg); 7911 default_console_color_bg = bg; 7912 default_console_color_fg = fg; 7913 # endif 7914 7915 set_console_color_rgb(); 7916 } 7917 7918 static void 7919 vtp_exit(void) 7920 { 7921 restore_console_color_rgb(); 7922 } 7923 7924 int 7925 vtp_printf( 7926 char *format, 7927 ...) 7928 { 7929 char_u buf[100]; 7930 va_list list; 7931 DWORD result; 7932 int len; 7933 7934 va_start(list, format); 7935 len = vim_vsnprintf((char *)buf, 100, (char *)format, list); 7936 va_end(list); 7937 WriteConsoleA(g_hConOut, buf, (DWORD)len, &result, NULL); 7938 return (int)result; 7939 } 7940 7941 static void 7942 vtp_sgr_bulk( 7943 int arg) 7944 { 7945 int args[1]; 7946 7947 args[0] = arg; 7948 vtp_sgr_bulks(1, args); 7949 } 7950 7951 #define FAST256(x) \ 7952 if ((*p-- = "0123456789"[(n = x % 10)]) \ 7953 && x >= 10 && (*p-- = "0123456789"[((m = x % 100) - n) / 10]) \ 7954 && x >= 100 && (*p-- = "012"[((x & 0xff) - m) / 100])); 7955 7956 #define FAST256CASE(x) \ 7957 case x: \ 7958 FAST256(newargs[x - 1]); 7959 7960 static void 7961 vtp_sgr_bulks( 7962 int argc, 7963 int *args) 7964 { 7965 #define MAXSGR 16 7966 #define SGRBUFSIZE 2 + 4 * MAXSGR + 1 // '\033[' + SGR + 'm' 7967 char_u buf[SGRBUFSIZE]; 7968 char_u *p; 7969 int in, out; 7970 int newargs[16]; 7971 static int sgrfgr = -1, sgrfgg, sgrfgb; 7972 static int sgrbgr = -1, sgrbgg, sgrbgb; 7973 7974 if (argc == 0) 7975 { 7976 sgrfgr = sgrbgr = -1; 7977 vtp_printf("033[m"); 7978 return; 7979 } 7980 7981 in = out = 0; 7982 while (in < argc) 7983 { 7984 int s = args[in]; 7985 int copylen = 1; 7986 7987 if (s == 38) 7988 { 7989 if (argc - in >= 5 && args[in + 1] == 2) 7990 { 7991 if (sgrfgr == args[in + 2] && sgrfgg == args[in + 3] 7992 && sgrfgb == args[in + 4]) 7993 { 7994 in += 5; 7995 copylen = 0; 7996 } 7997 else 7998 { 7999 sgrfgr = args[in + 2]; 8000 sgrfgg = args[in + 3]; 8001 sgrfgb = args[in + 4]; 8002 copylen = 5; 8003 } 8004 } 8005 else if (argc - in >= 3 && args[in + 1] == 5) 8006 { 8007 sgrfgr = -1; 8008 copylen = 3; 8009 } 8010 } 8011 else if (s == 48) 8012 { 8013 if (argc - in >= 5 && args[in + 1] == 2) 8014 { 8015 if (sgrbgr == args[in + 2] && sgrbgg == args[in + 3] 8016 && sgrbgb == args[in + 4]) 8017 { 8018 in += 5; 8019 copylen = 0; 8020 } 8021 else 8022 { 8023 sgrbgr = args[in + 2]; 8024 sgrbgg = args[in + 3]; 8025 sgrbgb = args[in + 4]; 8026 copylen = 5; 8027 } 8028 } 8029 else if (argc - in >= 3 && args[in + 1] == 5) 8030 { 8031 sgrbgr = -1; 8032 copylen = 3; 8033 } 8034 } 8035 else if (30 <= s && s <= 39) 8036 sgrfgr = -1; 8037 else if (90 <= s && s <= 97) 8038 sgrfgr = -1; 8039 else if (40 <= s && s <= 49) 8040 sgrbgr = -1; 8041 else if (100 <= s && s <= 107) 8042 sgrbgr = -1; 8043 else if (s == 0) 8044 sgrfgr = sgrbgr = -1; 8045 8046 while (copylen--) 8047 newargs[out++] = args[in++]; 8048 } 8049 8050 p = &buf[sizeof(buf) - 1]; 8051 *p-- = 'm'; 8052 8053 switch (out) 8054 { 8055 int n, m; 8056 DWORD r; 8057 8058 FAST256CASE(16); 8059 *p-- = ';'; 8060 FAST256CASE(15); 8061 *p-- = ';'; 8062 FAST256CASE(14); 8063 *p-- = ';'; 8064 FAST256CASE(13); 8065 *p-- = ';'; 8066 FAST256CASE(12); 8067 *p-- = ';'; 8068 FAST256CASE(11); 8069 *p-- = ';'; 8070 FAST256CASE(10); 8071 *p-- = ';'; 8072 FAST256CASE(9); 8073 *p-- = ';'; 8074 FAST256CASE(8); 8075 *p-- = ';'; 8076 FAST256CASE(7); 8077 *p-- = ';'; 8078 FAST256CASE(6); 8079 *p-- = ';'; 8080 FAST256CASE(5); 8081 *p-- = ';'; 8082 FAST256CASE(4); 8083 *p-- = ';'; 8084 FAST256CASE(3); 8085 *p-- = ';'; 8086 FAST256CASE(2); 8087 *p-- = ';'; 8088 FAST256CASE(1); 8089 *p-- = '['; 8090 *p = '\033'; 8091 WriteConsoleA(g_hConOut, p, (DWORD)(&buf[SGRBUFSIZE] - p), &r, NULL); 8092 default: 8093 break; 8094 } 8095 } 8096 8097 static void 8098 wt_init(void) 8099 { 8100 wt_working = (mch_getenv("WT_SESSION") != NULL); 8101 } 8102 8103 int 8104 use_wt(void) 8105 { 8106 return USE_WT; 8107 } 8108 8109 # ifdef FEAT_TERMGUICOLORS 8110 static int 8111 ctermtoxterm( 8112 int cterm) 8113 { 8114 char_u r, g, b, idx; 8115 8116 cterm_color2rgb(cterm, &r, &g, &b, &idx); 8117 return (((int)r << 16) | ((int)g << 8) | (int)b); 8118 } 8119 # endif 8120 8121 static void 8122 set_console_color_rgb(void) 8123 { 8124 # ifdef FEAT_TERMGUICOLORS 8125 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi; 8126 guicolor_T fg, bg; 8127 int ctermfg, ctermbg; 8128 8129 if (!USE_VTP) 8130 return; 8131 8132 get_default_console_color(&ctermfg, &ctermbg, &fg, &bg); 8133 8134 if (USE_WT) 8135 { 8136 term_fg_rgb_color(fg); 8137 term_bg_rgb_color(bg); 8138 return; 8139 } 8140 8141 fg = (GetRValue(fg) << 16) | (GetGValue(fg) << 8) | GetBValue(fg); 8142 bg = (GetRValue(bg) << 16) | (GetGValue(bg) << 8) | GetBValue(bg); 8143 8144 csbi.cbSize = sizeof(csbi); 8145 if (has_csbiex) 8146 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi); 8147 8148 csbi.cbSize = sizeof(csbi); 8149 csbi.srWindow.Right += 1; 8150 csbi.srWindow.Bottom += 1; 8151 store_console_bg_rgb = csbi.ColorTable[g_color_index_bg]; 8152 store_console_fg_rgb = csbi.ColorTable[g_color_index_fg]; 8153 csbi.ColorTable[g_color_index_bg] = (COLORREF)bg; 8154 csbi.ColorTable[g_color_index_fg] = (COLORREF)fg; 8155 if (has_csbiex) 8156 pSetConsoleScreenBufferInfoEx(g_hConOut, &csbi); 8157 # endif 8158 } 8159 8160 # if defined(FEAT_TERMGUICOLORS) || defined(PROTO) 8161 void 8162 get_default_console_color( 8163 int *cterm_fg, 8164 int *cterm_bg, 8165 guicolor_T *gui_fg, 8166 guicolor_T *gui_bg) 8167 { 8168 int id; 8169 guicolor_T guifg = INVALCOLOR; 8170 guicolor_T guibg = INVALCOLOR; 8171 int ctermfg = 0; 8172 int ctermbg = 0; 8173 8174 id = syn_name2id((char_u *)"Normal"); 8175 if (id > 0 && p_tgc) 8176 syn_id2colors(id, &guifg, &guibg); 8177 if (guifg == INVALCOLOR) 8178 { 8179 ctermfg = -1; 8180 if (id > 0) 8181 syn_id2cterm_bg(id, &ctermfg, &ctermbg); 8182 guifg = ctermfg != -1 ? ctermtoxterm(ctermfg) 8183 : default_console_color_fg; 8184 cterm_normal_fg_gui_color = guifg; 8185 ctermfg = ctermfg < 0 ? 0 : ctermfg; 8186 } 8187 if (guibg == INVALCOLOR) 8188 { 8189 ctermbg = -1; 8190 if (id > 0) 8191 syn_id2cterm_bg(id, &ctermfg, &ctermbg); 8192 guibg = ctermbg != -1 ? ctermtoxterm(ctermbg) 8193 : default_console_color_bg; 8194 cterm_normal_bg_gui_color = guibg; 8195 ctermbg = ctermbg < 0 ? 0 : ctermbg; 8196 } 8197 8198 *cterm_fg = ctermfg; 8199 *cterm_bg = ctermbg; 8200 *gui_fg = guifg; 8201 *gui_bg = guibg; 8202 } 8203 # endif 8204 8205 /* 8206 * Set the console colors to the original colors or the last set colors. 8207 */ 8208 static void 8209 reset_console_color_rgb(void) 8210 { 8211 # ifdef FEAT_TERMGUICOLORS 8212 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi; 8213 8214 if (USE_WT) 8215 return; 8216 8217 csbi.cbSize = sizeof(csbi); 8218 if (has_csbiex) 8219 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi); 8220 8221 csbi.cbSize = sizeof(csbi); 8222 csbi.srWindow.Right += 1; 8223 csbi.srWindow.Bottom += 1; 8224 csbi.ColorTable[g_color_index_bg] = (COLORREF)store_console_bg_rgb; 8225 csbi.ColorTable[g_color_index_fg] = (COLORREF)store_console_fg_rgb; 8226 if (has_csbiex) 8227 pSetConsoleScreenBufferInfoEx(g_hConOut, &csbi); 8228 # endif 8229 } 8230 8231 /* 8232 * Set the console colors to the original colors. 8233 */ 8234 static void 8235 restore_console_color_rgb(void) 8236 { 8237 # ifdef FEAT_TERMGUICOLORS 8238 DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi; 8239 8240 csbi.cbSize = sizeof(csbi); 8241 if (has_csbiex) 8242 pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi); 8243 8244 csbi.cbSize = sizeof(csbi); 8245 csbi.srWindow.Right += 1; 8246 csbi.srWindow.Bottom += 1; 8247 csbi.ColorTable[g_color_index_bg] = (COLORREF)save_console_bg_rgb; 8248 csbi.ColorTable[g_color_index_fg] = (COLORREF)save_console_fg_rgb; 8249 if (has_csbiex) 8250 pSetConsoleScreenBufferInfoEx(g_hConOut, &csbi); 8251 # endif 8252 } 8253 8254 void 8255 control_console_color_rgb(void) 8256 { 8257 if (USE_VTP) 8258 set_console_color_rgb(); 8259 else 8260 reset_console_color_rgb(); 8261 } 8262 8263 int 8264 use_vtp(void) 8265 { 8266 return USE_VTP; 8267 } 8268 8269 int 8270 is_term_win32(void) 8271 { 8272 return T_NAME != NULL && STRCMP(T_NAME, "win32") == 0; 8273 } 8274 8275 int 8276 has_vtp_working(void) 8277 { 8278 return vtp_working; 8279 } 8280 8281 #endif 8282 8283 int 8284 has_conpty_working(void) 8285 { 8286 return conpty_working; 8287 } 8288 8289 int 8290 get_conpty_type(void) 8291 { 8292 return conpty_type; 8293 } 8294 8295 int 8296 is_conpty_stable(void) 8297 { 8298 return conpty_stable; 8299 } 8300 8301 int 8302 get_conpty_fix_type(void) 8303 { 8304 return conpty_fix_type; 8305 } 8306 8307 #if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) || defined(PROTO) 8308 void 8309 resize_console_buf(void) 8310 { 8311 CONSOLE_SCREEN_BUFFER_INFO csbi; 8312 COORD coord; 8313 SMALL_RECT newsize; 8314 8315 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi)) 8316 { 8317 coord.X = SRWIDTH(csbi.srWindow); 8318 coord.Y = SRHEIGHT(csbi.srWindow); 8319 SetConsoleScreenBufferSize(g_hConOut, coord); 8320 8321 newsize.Left = 0; 8322 newsize.Top = 0; 8323 newsize.Right = coord.X - 1; 8324 newsize.Bottom = coord.Y - 1; 8325 SetConsoleWindowInfo(g_hConOut, TRUE, &newsize); 8326 8327 SetConsoleScreenBufferSize(g_hConOut, coord); 8328 } 8329 } 8330 #endif 8331