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