int apiGetFullPathName(LPCWSTR lpFileName, CEStr& rsPath) { bool bFound = false; wchar_t *pszFilePart, *pszBuffer = NULL; wchar_t szFind[MAX_PATH+1]; DWORD nLen = GetFullPathName(lpFileName, countof(szFind), szFind, &pszFilePart); if (nLen) { if (nLen < countof(szFind)) { bFound = true; rsPath.Set(szFind); } else { // Too long path, allocate more space pszBuffer = (wchar_t*)malloc((++nLen)*sizeof(*pszBuffer)); if (pszBuffer) { DWORD nLen2 = GetFullPathName(lpFileName, nLen, pszBuffer, &pszFilePart); if (nLen2 && (nLen2 < nLen)) { bFound = true; rsPath.Set(pszBuffer); } free(pszBuffer); } } } return bFound ? rsPath.GetLen() : 0; }
bool IsNeedCmd(BOOL bRootCmd, LPCWSTR asCmdLine, CEStr &szExe, LPCWSTR* rsArguments /*= NULL*/, BOOL* rpbNeedCutStartEndQuot /*= NULL*/, BOOL* rpbRootIsCmdExe /*= NULL*/, BOOL* rpbAlwaysConfirmExit /*= NULL*/, BOOL* rpbAutoDisableConfirmExit /*= NULL*/) { bool rbNeedCutStartEndQuot = false; bool rbRootIsCmdExe = true; bool rbAlwaysConfirmExit = false; bool rbAutoDisableConfirmExit = false; wchar_t *pwszEndSpace; if (rsArguments) *rsArguments = NULL; bool lbRc = false; BOOL lbFirstWasGot = FALSE; LPCWSTR pwszCopy; int nLastChar; #ifdef _DEBUG CmdArg szDbgFirst; #endif if (!asCmdLine || !*asCmdLine) { _ASSERTE(asCmdLine && *asCmdLine); goto wrap; } #ifdef _DEBUG // Это минимальные проверки, собственно к коду - не относятся bool bIsBatch = false; { NextArg(asCmdLine, szDbgFirst); LPCWSTR psz = PointToExt(szDbgFirst); if (lstrcmpi(psz, L".cmd")==0 || lstrcmpi(psz, L".bat")==0) bIsBatch = true; } #endif if (!szExe.GetBuffer(MAX_PATH)) { _ASSERTE(FALSE && "Failed to allocate MAX_PATH"); lbRc = true; goto wrap; } szExe.Empty(); if (!asCmdLine || *asCmdLine == 0) { _ASSERTE(asCmdLine && *asCmdLine); lbRc = true; goto wrap; } pwszCopy = asCmdLine; // cmd /c ""c:\program files\arc\7z.exe" -?" // да еще и внутри могут быть двойными... // cmd /c "dir c:\" nLastChar = lstrlenW(pwszCopy) - 1; if (pwszCopy[0] == L'"' && pwszCopy[nLastChar] == L'"') { //if (pwszCopy[1] == L'"' && pwszCopy[2]) //{ // pwszCopy ++; // Отбросить первую кавычку в командах типа: ""c:\program files\arc\7z.exe" -?" // if (rbNeedCutStartEndQuot) *rbNeedCutStartEndQuot = TRUE; //} //else // глючила на ""F:\VCProject\FarPlugin\#FAR180\far.exe -new_console"" //if (wcschr(pwszCopy+1, L'"') == (pwszCopy+nLastChar)) { // LPCWSTR pwszTemp = pwszCopy; // // Получим первую команду (исполняемый файл?) // if ((iRc = NextArg(&pwszTemp, szArg)) != 0) { // //Parsing command line failed // lbRc = true; goto wrap; // } // pwszCopy ++; // Отбросить первую кавычку в командах типа: "c:\arc\7z.exe -?" // lbFirstWasGot = TRUE; // if (rbNeedCutStartEndQuot) *rbNeedCutStartEndQuot = TRUE; //} else { // Will be dequoted in 'NextArg' function. Examples // "C:\GCC\msys\bin\make.EXE -f "makefile" COMMON="../../../plugins/common"" // ""F:\VCProject\FarPlugin\#FAR180\far.exe -new_console"" // ""cmd"" // cmd /c ""c:\program files\arc\7z.exe" -?" // да еще и внутри могут быть двойными... // cmd /c "dir c:\" // Получим первую команду (исполняемый файл?) CmdArg arg; if (!NextArg(pwszCopy, arg)) { //Parsing command line failed #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } szExe.Set(arg); if (lstrcmpiW(szExe, L"start") == 0) { // Команду start обрабатывает только процессор #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } LPCWSTR pwszQ = pwszCopy + 1 + lstrlen(szExe); wchar_t* pszExpand = NULL; if (*pwszQ != L'"' && IsExecutable(szExe, &pszExpand)) { pwszCopy ++; // отбрасываем lbFirstWasGot = TRUE; if (pszExpand) { szExe.Set(pszExpand); SafeFree(pszExpand); if (rsArguments) *rsArguments = pwszQ; } rbNeedCutStartEndQuot = true; } } } // Получим первую команду (исполняемый файл?) if (!lbFirstWasGot) { szExe.Empty(); // `start` command must be processed by processor itself if ((lstrcmpni(pwszCopy, L"start", 5) == 0) && (!pwszCopy[5] || isSpace(pwszCopy[5]))) { #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } // 17.10.2010 - support executable file path without parameters, but with spaces in its path // 22.11.2015 - or some weirdness, like `C:\Program Files\CB/cb_console_runner.exe "C:\sources\app.exe"` LPCWSTR pchEnd = wcschr(pwszCopy, L' '); if (!pchEnd) pchEnd = pwszCopy + lstrlenW(pwszCopy); CEStr szTemp; DWORD nTempSize; while (pchEnd) { szTemp.Set(pwszCopy, (pchEnd - pwszCopy)); _ASSERTE(szTemp[(pchEnd - pwszCopy)] == 0); // Argument was quoted? if (!szTemp.IsEmpty()) { INT_PTR len = szTemp.GetLen(); if ((len > 2) && (szTemp[0] == L'"') && (szTemp[len-1] == L'"')) { memmove(szTemp.ms_Val, szTemp.ms_Val+1, (len-2)*sizeof(*szTemp.ms_Val)); szTemp.ms_Val[len-2] = 0; } } // If this is a full path without environment variables if (!szTemp.IsEmpty() && ((IsFilePath(szTemp, true) && !wcschr(szTemp, L'%')) // or file/dir may be found via env.var. substitution or searching in %PATH% || FileExistsSearch((LPCWSTR)szTemp, szTemp)) // Than check if it is a FILE (not a directory) && FileExists(szTemp, &nTempSize) && nTempSize) { // OK, it an our executable? if (rsArguments) *rsArguments = pchEnd; szExe.Set(szTemp); break; } _ASSERTE(*pchEnd == 0 || *pchEnd == L' '); if (!*pchEnd) break; // Find next space after nonspace while (*(pchEnd) == L' ') pchEnd++; // If quoted string starts from here - it's supposed to be an argument if (*pchEnd == L'"') { // And we must not get here, because the executable must be already processed above // _ASSERTE(*pchEnd != L'"'); break; } pchEnd = wcschr(pchEnd, L' '); if (!pchEnd) pchEnd = pwszCopy + lstrlenW(pwszCopy); } if (szExe.IsEmpty()) { CmdArg arg; if (!(pwszCopy = NextArg(pwszCopy, arg))) { //Parsing command line failed #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } szExe.Set(arg); _ASSERTE(lstrcmpiW(szExe, L"start") != 0); // Обработка переменных окружения и поиск в PATH if (FileExistsSearch((LPCWSTR)szExe, szExe)) { if (rsArguments) *rsArguments = pwszCopy; } } } if (!*szExe) { _ASSERTE(szExe[0] != 0); } else { // Если юзеру нужен редирект - то он должен явно указать ком.процессор // Иначе нереально (или достаточно сложно) определить, является ли символ // редиректом, или это просто один из аргументов команды... // "Левые" символы в имени файла (а вот в "первом аргументе" все однозначно) if (wcspbrk(szExe, L"?*<>|")) { rbRootIsCmdExe = TRUE; // запуск через "процессор" lbRc = true; goto wrap; // добавить "cmd.exe" } // если "путь" не указан if (wcschr(szExe, L'\\') == NULL) { bool bHasExt = (wcschr(szExe, L'.') != NULL); // Проверим, может это команда процессора (типа "DIR")? if (!bHasExt) { bool bIsCommand = false; wchar_t* pszUppr = lstrdup(szExe); if (pszUppr) { // избежать линковки на user32.dll //CharUpperBuff(pszUppr, lstrlen(pszUppr)); for (wchar_t* p = pszUppr; *p; p++) { if (*p >= L'a' && *p <= 'z') *p -= 0x20; } const wchar_t* pszFind = gsInternalCommands; while (*pszFind) { if (lstrcmp(pszUppr, pszFind) == 0) { bIsCommand = true; break; } pszFind += lstrlen(pszFind)+1; } free(pszUppr); } if (bIsCommand) { #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif rbRootIsCmdExe = TRUE; // запуск через "процессор" lbRc = true; goto wrap; // добавить "cmd.exe" } } // Try to find executable in %PATH% { #ifndef CONEMU_MINIMAL MWow64Disable wow; wow.Disable(); // Disable Wow64 file redirector #endif apiSearchPath(NULL, szExe, bHasExt ? NULL : L".exe", szExe); } } // end: if (wcschr(szExe, L'\\') == NULL) } // Если szExe не содержит путь к файлу - запускаем через cmd // "start "" C:\Utils\Files\Hiew32\hiew32.exe C:\00\Far.exe" if (!IsFilePath(szExe)) { #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif rbRootIsCmdExe = TRUE; // запуск через "процессор" lbRc = true; goto wrap; // добавить "cmd.exe" } //pwszCopy = wcsrchr(szArg, L'\\'); if (!pwszCopy) pwszCopy = szArg; else pwszCopy ++; pwszCopy = PointToName(szExe); //2009-08-27 pwszEndSpace = szExe.ms_Val + lstrlenW(szExe) - 1; while ((*pwszEndSpace == L' ') && (pwszEndSpace > szExe)) { *(pwszEndSpace--) = 0; } #ifndef __GNUC__ #pragma warning( push ) #pragma warning(disable : 6400) #endif if (lstrcmpiW(pwszCopy, L"cmd")==0 || lstrcmpiW(pwszCopy, L"cmd.exe")==0 || lstrcmpiW(pwszCopy, L"tcc")==0 || lstrcmpiW(pwszCopy, L"tcc.exe")==0) { rbRootIsCmdExe = TRUE; // уже должен быть выставлен, но проверим rbAlwaysConfirmExit = TRUE; rbAutoDisableConfirmExit = FALSE; _ASSERTE(!bIsBatch); lbRc = false; goto wrap; // уже указан командный процессор, cmd.exe в начало добавлять не нужно } // Issue 1211: Decide not to do weird heuristic. // If user REALLY needs redirection (root command, huh?) // - he must call "cmd /c ..." directly // Если есть одна из команд перенаправления, или слияния - нужен CMD.EXE if (!bRootCmd) { if (wcschr(asCmdLine, L'&') || wcschr(asCmdLine, L'>') || wcschr(asCmdLine, L'<') || wcschr(asCmdLine, L'|') || wcschr(asCmdLine, L'^') // или экранирования ) { #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } } //if (lstrcmpiW(pwszCopy, L"far")==0 || lstrcmpiW(pwszCopy, L"far.exe")==0) if (IsFarExe(pwszCopy)) { bool bFound = (wcschr(pwszCopy, L'.') != NULL); // Если указали при запуске просто "far" - это может быть батник, расположенный в %PATH% if (!bFound) { CEStr szSearch; if (apiSearchPath(NULL, pwszCopy, L".exe", szSearch)) { if (lstrcmpi(PointToExt(szSearch), L".exe") == 0) bFound = true; } } if (bFound) { rbAutoDisableConfirmExit = TRUE; rbRootIsCmdExe = FALSE; // FAR! _ASSERTE(!bIsBatch); lbRc = false; goto wrap; // уже указан исполняемый файл, cmd.exe в начало добавлять не нужно } } if (IsExecutable(szExe)) { rbRootIsCmdExe = FALSE; // Для других программ - буфер не включаем _ASSERTE(!bIsBatch); lbRc = false; goto wrap; // Запускается конкретная консольная программа. cmd.exe не требуется } //Можно еще Доделать поиски с: SearchPath, GetFullPathName, добавив расширения .exe & .com //хотя фар сам формирует полные пути к командам, так что можно не заморачиваться #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif rbRootIsCmdExe = TRUE; #ifndef __GNUC__ #pragma warning( pop ) #endif lbRc = true; wrap: if (rpbNeedCutStartEndQuot) *rpbNeedCutStartEndQuot = rbNeedCutStartEndQuot; if (rpbRootIsCmdExe) *rpbRootIsCmdExe = rbRootIsCmdExe; if (rpbAlwaysConfirmExit) *rpbAlwaysConfirmExit = rbAlwaysConfirmExit; if (rpbAutoDisableConfirmExit) *rpbAutoDisableConfirmExit = rbAutoDisableConfirmExit; return lbRc; }
// Returns true on changes // bDeQuote: replace two "" with one " // bDeEscape: process special symbols: ^e^[^r^n^t^b bool DemangleArg(CEStr& rsDemangle, bool bDeQuote /*= true*/, bool bDeEscape /*= false*/) { if (rsDemangle.IsEmpty() || !(bDeQuote || bDeEscape)) { return false; // Nothing to do } LPCWSTR pszDemangles = (bDeQuote && bDeEscape) ? L"^\"" : bDeQuote ? L"\"" : L"^"; LPCWSTR pchCap = wcspbrk(rsDemangle, pszDemangles); if (pchCap == NULL) { return false; // Nothing to replace } wchar_t* pszDst = rsDemangle.ms_Val; const wchar_t* pszSrc = rsDemangle.ms_Val; const wchar_t* pszEnd = rsDemangle.ms_Val + rsDemangle.GetLen(); while (pszSrc < pszEnd) { if (bDeQuote && (*pszSrc == L'"')) { *(pszDst++) = *(pszSrc++); if (*pszSrc == L'"') // Expected, but may be missed by user? pszSrc++; } else if (bDeEscape && (*pszSrc == L'^')) { switch (*(++pszSrc)) { case L'^': // Demangle cap *pszDst = L'^'; break; case 0: // Leave single final cap *pszDst = L'^'; continue; case L'r': case L'R': // CR *pszDst = L'\r'; break; case L'n': case L'N': // LF *pszDst = L'\n'; break; case L't': case L'T': // TAB *pszDst = L'\t'; break; case L'a': case L'A': // BELL *pszDst = 7; break; case L'b': case L'B': // BACK *pszDst = L'\b'; break; case L'e': case L'E': case L'[': // ESC *pszDst = 27; break; default: // Unknown ctrl-sequence, bypass *(pszDst++) = *(pszSrc++); continue; } pszDst++; pszSrc++; } else { *(pszDst++) = *(pszSrc++); } } // Was processed? Zero terminate it. *pszDst = 0; return true; }
bool TermX::GetSubstitute(const MOUSE_EVENT_RECORD& m, TermMouseMode MouseMode, CEStr& lsSubst) { _ASSERTE(lsSubst.IsEmpty()); if (!MouseMode) { lsSubst.Clear(); return false; } wchar_t szSubst[16] = L""; // Deprecated. Mouse events mimic xterm behavior now. #if 0 // http://conemu.github.io/en/VimXterm.html#Vim-scrolling-using-mouse-Wheel // https://github.com/Maximus5/ConEmu/issues/1007 if ((m.dwEventFlags & MOUSE_WHEELED) && (MouseMode & tmm_VIM)) { // If the high word of the dwButtonState member contains // a positive value, the wheel was rotated forward, away from the user. // Otherwise, the wheel was rotated backward, toward the user. short dir = (short)HIWORD(m.dwButtonState); // Ctrl/Alt/Shift DWORD mods = (m.dwControlKeyState & (LEFT_ALT_PRESSED|LEFT_CTRL_PRESSED|RIGHT_ALT_PRESSED|RIGHT_CTRL_PRESSED|SHIFT_PRESSED)); if (mods == 0) { if (dir <= 0) wcscpy_c(szSubst, L"\033[62~"); // <MouseDown> else wcscpy_c(szSubst, L"\033[63~"); // <MouseUp> } else if (mods == SHIFT_PRESSED) { if (dir <= 0) wcscpy_c(szSubst, L"\033[64~"); // <S-MouseDown> else wcscpy_c(szSubst, L"\033[65~"); // <S-MouseUp> } else return false; lsSubst.Set(szSubst); return true; } #endif if ((m.dwEventFlags & MOUSE_WHEELED) && (MouseMode & tmm_SCROLL)) { KEY_EVENT_RECORD k = {TRUE, 1}; short dir = (short)HIWORD(m.dwButtonState); DWORD mods = (m.dwControlKeyState & (LEFT_ALT_PRESSED|LEFT_CTRL_PRESSED|RIGHT_ALT_PRESSED|RIGHT_CTRL_PRESSED|SHIFT_PRESSED)); UINT nCount = 1; if (mods == 0) { k.wVirtualKeyCode = (dir > 0) ? VK_UP : VK_DOWN; nCount = gpConEmu->mouse.GetWheelScrollLines(); } else if (mods == SHIFT_PRESSED) { k.wVirtualKeyCode = (dir > 0) ? VK_PRIOR : VK_NEXT; } else return false; CEStr lsPart; if (!GetSubstitute(k, lsPart) || lsPart.IsEmpty()) return false; if (nCount > 1) { INT_PTR l = lsPart.GetLen(); wchar_t* ptr = lsSubst.GetBuffer(l*nCount); for (UINT i = 1; i <= nCount; ++i, ptr += l) wcscpy_s(ptr, l+1, lsPart); } else { lsSubst.Attach(lsPart.Detach()); } return true; } if (!(MouseMode & ~(tmm_VIM|tmm_SCROLL))) return false; BYTE NewBtns = (m.dwButtonState & 0x1F); if ((NewBtns != MouseButtons) || ((m.dwEventFlags & MOUSE_WHEELED) && HIWORD(m.dwButtonState)) || ((LastMousePos != m.dwMousePosition) && ((MouseMode & tmm_ANY) || ((MouseMode & tmm_BTN) && NewBtns))) ) { // #XTERM_MOUSE Unfortunately, szSubst is too short to pass "missed" coordinates // Like we do for Far events, MouseMove with RBtn pressed in tmm_ANY|tmm_BTN // modes would send all intermediate coordinates between two events BYTE code; bool released = false; if (NewBtns & FROM_LEFT_1ST_BUTTON_PRESSED) code = 0; // MB1 pressed else if (NewBtns & FROM_LEFT_2ND_BUTTON_PRESSED) code = 1; // MB2 pressed else if (NewBtns & RIGHTMOST_BUTTON_PRESSED) code = 2; // MB3 pressed else if (NewBtns & FROM_LEFT_3RD_BUTTON_PRESSED) code = 64; // MB4 pressed else if (NewBtns & FROM_LEFT_4TH_BUTTON_PRESSED) code = 64 + 1; // MB5 pressed else if (m.dwEventFlags & MOUSE_WHEELED) { // #XTERM_MOUSE Post multiple events if dir contains multiple notches short dir = (short)HIWORD(m.dwButtonState); if (dir > 0) code = 64; // MB4 else if (dir < 0) code = 64 + 1; // MB5 } else { released = true; code = 3; } if (m.dwControlKeyState & SHIFT_PRESSED) code |= 4; if (m.dwControlKeyState & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED)) code |= 8; if (m.dwControlKeyState & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)) code |= 16; if ((m.dwEventFlags & MOUSE_MOVED) && (MouseMode & (tmm_BTN|tmm_ANY))) code |= 32; // (1,1) is upper left character position SHORT coord[] = {std::max<SHORT>(0, m.dwMousePosition.X) + 1, std::max<SHORT>(0, m.dwMousePosition.Y) + 1}; if (MouseMode & tmm_XTERM) { msprintf(szSubst, countof(szSubst), L"\033[<%u;%u;%u%c", code, coord[0], coord[1], released ? 'm' : 'M'); } else if (MouseMode & tmm_URXVT) { msprintf(szSubst, countof(szSubst), L"\033[%u;%u;%uM", code + 0x20, coord[0], coord[1]); } else { wcscpy_c(szSubst, L"\033[M"); size_t i = wcslen(szSubst); szSubst[i++] = code + 32; // And coords. Must be in "screen" coordinate space for (size_t s = 0; s < 2; ++s) { // (1,1) is upper left character position if (!(MouseMode & tmm_UTF8)) szSubst[i++] = std::min<unsigned>(255, coord[s] + 32); else if (coord[s] < 0x80) szSubst[i++] = coord[s]; else if (coord[s] < 0x800) { // xterm #262: positions from 96 to 2015 are encoded as a two-byte UTF-8 sequence szSubst[i++] = 0xC0 + (coord[s] >> 6); szSubst[i++] = 0x80 + (coord[s] & 0x3F); } else { // #XTERM_MOUSE Xterm reports out-of-range positions as a NUL byte. szSubst[i++] = 1; // It's impossible to post NUL byte ATM } }