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