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