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