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