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