void DebugNeedCmdUnitTests() { BOOL b; struct strTests { LPCWSTR pszCmd; BOOL bNeed; } Tests[] = { {L"\"C:\\windows\\notepad.exe -f \"makefile\" COMMON=\"../../../plugins/common\"\"", FALSE}, {L"\"\"C:\\windows\\notepad.exe -new_console\"\"", FALSE}, {L"\"\"cmd\"\"", FALSE}, {L"cmd /c \"\"C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe\" -?\"", FALSE}, {L"cmd /c \"dir c:\\\"", FALSE}, {L"abc.cmd", TRUE}, // Do not do too many heuristic. If user really needs redirection (for 'root'!) // he must explicitly call "cmd /c ...". With only exception if first exe not found. {L"notepad text & start explorer", FALSE}, }; LPCWSTR psArgs; BOOL bNeedCut, bRootIsCmd, bAlwaysConfirm, bAutoDisable; CmdArg szExe; for (INT_PTR i = 0; i < countof(Tests); i++) { szExe.Empty(); RConStartArgs rcs; rcs.pszSpecialCmd = lstrdup(Tests[i].pszCmd); rcs.ProcessNewConArg(); b = IsNeedCmd(TRUE, rcs.pszSpecialCmd, szExe, &psArgs, &bNeedCut, &bRootIsCmd, &bAlwaysConfirm, &bAutoDisable); _ASSERTE(b == Tests[i].bNeed); } }
//------- // ^FUNCTION: CmdLine::print_descriptions // // ^SYNOPSIS: // unsigned CmdLine::print_descriptions(syntax, os, cols, longest) // // ^PARAMETERS: // CmdLine::CmdLineSyntax syntax; // -- the syntax to use (long-option, short-option, or both) // when printing the synopsis. // // ostream & os; // -- where to print. // // int cols; // -- the maximum width of a line. // // unsigned longest; // -- value returned by print_synopsis. // // ^DESCRIPTION: // Print a command argument descriptions (using the command-line syntax). // The descriptions should be printed to "os" using the desired syntax, // in lines that are no more than "cols" characters wide. // // ^REQUIREMENTS: // "longest" should correspond to a value returned by print_synopsis // that used the same "cmd" and syntax. // // ^SIDE-EFFECTS: // Prints on "os". // // ^RETURN-VALUE: // None. // // ^ALGORITHM: // Print the description for each argument. //-^^---- void CmdLine::print_descriptions(CmdLine::CmdLineSyntax syntax, ostream & os, int cols, unsigned longest) const { int positionals, options = 0; char buf[256]; for (positionals = 0 ; positionals < 2 ; positionals++) { CmdArgListListIter list_iter(cmd_args); for (CmdArgList * alist = list_iter() ; alist ; alist = list_iter()) { CmdArgListIter iter(alist); for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) { // don't display hidden arguments if (cmdarg->syntax() & CmdArg::isHIDDEN) continue; if (!positionals && (cmdarg->syntax() & CmdArg::isPOS)) continue; if (positionals && !(cmdarg->syntax() & CmdArg::isPOS)) continue; #ifdef vms_style if ( !options++ ) os << "Qualifiers/Parameters:\n" ; #else if ( !options++ ) os << "Options/Arguments:\n" ; #endif if (! fmt_arg(cmdarg, buf, sizeof(buf), syntax, TERSE_USAGE)) { continue; } const char * description = cmdarg->description(); if ((description == NULL) || (! *description)) continue; strindent(os, cols, 8, buf, (longest + 2), description); } //for each cmdarg } //for each arg-list } //for each parm-type }
bool FileExistsSearch(LPCWSTR asFilePath, CmdArg& rsFound, bool abSetPath/*= true*/, bool abRegSearch /*= true*/) { if (!asFilePath || !*asFilePath) { _ASSERTEX(asFilePath && *asFilePath); return false; } if (FileExists(asFilePath)) { return true; } // Развернуть переменные окружения if (wcschr(asFilePath, L'%')) { bool bFound = false; wchar_t* pszExpand = ExpandEnvStr(asFilePath); if (pszExpand && FileExists(pszExpand)) { // asFilePath will be invalid after .Set rsFound.Set(pszExpand); bFound = true; } SafeFree(pszExpand); if (bFound) { return true; } } // Search "Path" if (FileSearchInDir(asFilePath, rsFound)) { return true; } // Только если приложение не нашли "по путям" - пытаемся определить его расположение через [App Paths] // В противном случае, в частности, может быть запущен "far" не из папки с ConEmu, а зарегистрированный // в реестре, если на машине их несколько установлено. #if !defined(CONEMU_MINIMAL) // В ConEmuHk этот блок не активен, потому что может быть "только" перехват CreateProcess, // а о его параметрах должно заботиться вызывающее (текущее) приложение if (abRegSearch && !wcschr(asFilePath, L'\\')) { // Если в asFilePath НЕ указан путь - искать приложение в реестре, // и там могут быть указаны доп. параметры (пока только добавка в %PATH%) if (SearchAppPaths(asFilePath, rsFound, abSetPath)) { // Нашли по реестру, возможно обновили %PATH% return true; } } #endif return false; }
bool FileSearchInDir(LPCWSTR asFilePath, CmdArg& rsFound) { // Possibilities // a) asFilePath does not contain path, only: "far" // b) asFilePath contains path, but without extension: "C:\\Program Files\\Far\\Far" LPCWSTR pszSearchFile = asFilePath; LPCWSTR pszSlash = wcsrchr(asFilePath, L'\\'); if (pszSlash) { if ((pszSlash - asFilePath) >= MAX_PATH) { // No need to continue, this is invalid path to executable return false; } // C:\Far\Far.exe /w /pC:\Far\Plugins\ConEmu;C:\Far\Plugins.My if (!IsFilePath(asFilePath, false)) { return false; } // Do not allow '/' here LPCWSTR pszFwd = wcschr(asFilePath, L'/'); if (pszFwd != NULL) { return false; } } wchar_t* pszSearchPath = NULL; if (pszSlash) { if ((pszSearchPath = lstrdup(asFilePath)) != NULL) { pszSearchFile = pszSlash + 1; pszSearchPath[pszSearchFile - asFilePath] = 0; } } // Попытаемся найти "по путям" (%PATH%) wchar_t *pszFilePart; wchar_t szFind[MAX_PATH+1]; LPCWSTR pszExt = PointToExt(asFilePath); DWORD nLen = SearchPath(pszSearchPath, pszSearchFile, pszExt ? NULL : L".exe", countof(szFind), szFind, &pszFilePart); SafeFree(pszSearchPath); if (nLen && (nLen < countof(szFind)) && FileExists(szFind)) { // asFilePath will be invalid after .Set rsFound.Set(szFind); return true; } return false; }
//------- // ^FUNCTION: CmdLine::pos_match - match a positional argument // // ^SYNOPSIS: // CmdArg * CmdLine::pos_match(void) // // ^PARAMETERS: // // ^DESCRIPTION: // If "cmd" has an positional argument that has not yet been given, // or that corresponds to a list, then this function will find and // return the first such argument. // // ^REQUIREMENTS: // // ^SIDE-EFFECTS: // None. // // ^RETURN-VALUE: // If we find a match, then we return a pointer to its argument, // otherwise we return NULL. // // ^ALGORITHM: // First look for the first unmatched positional argument!! // If there aren't any, then look for the LAST positional list! //-^^---- CmdArg * CmdLine::pos_match(void) const { CmdArg * last_pos_list = NULL; CmdArgListListIter list_iter(cmd_args); for (CmdArgList * alist = list_iter() ; alist ; alist = list_iter()) { CmdArgListIter iter(alist); for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) { if (cmdarg->is_dummy()) continue; if (cmdarg->syntax() & CmdArg::isPOS) { if (! (cmdarg->flags() & CmdArg::GIVEN)) { return cmdarg ; } else if (cmdarg->flags() & CmdArg::isLIST) { last_pos_list = cmdarg; } } } //for iter } //for list_iter return last_pos_list ; }
//------- // ^FUNCTION: CmdLine::opt_match - attempt to match on option // // ^SYNOPSIS: // CmdArg * CmdLine::opt_match(optchar); // // ^PARAMETERS: // char optchar; // -- a possible option for "cmd" // // ^DESCRIPTION: // If "cmd" has an argument that has "optchar" as a single-character // option then this function will find and return that argument. // // ^REQUIREMENTS: // // ^SIDE-EFFECTS: // None. // // ^RETURN-VALUE: // If we find a match, then we return a pointer to its argdesc, // otherwise we return NULL. // // ^ALGORITHM: // Trivial. //-^^---- CmdArg * CmdLine::opt_match(char optchar) const { CmdArgListListIter list_iter(cmd_args); for (CmdArgList * alist = list_iter() ; alist ; alist = list_iter()) { CmdArgListIter iter(alist); for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) { if (cmdarg->is_dummy()) continue; if (optchar == cmdarg->char_name()) { // exact match return cmdarg ; } else if (cmd_flags & ANY_CASE_OPTS) { // case-insensitive match if (TO_LOWER(optchar) == TO_LOWER(cmdarg->char_name())) { return cmdarg ; } } } //for iter } //for list_iter return NULL ; }
//------- // ^FUNCTION: CmdLine::missing_args - check for missing required arguments // // ^SYNOPSIS: // unsigned CmdLine::missing_args(void); // // ^PARAMETERS: // // ^DESCRIPTION: // This function checks to see if there is a required argument in the // CmdLine object that was NOT specified on the command. If this is // the case and PROMPT_USER is set (or $PROMPT_USER exists and is // non-empty) then we attempt to prompt the user for the missing argument. // // ^REQUIREMENTS: // // ^SIDE-EFFECTS: // - modifies the status of "cmd". // - terminates execution by calling quit() if cmd_NOABORT is NOT // set and a required argument (that was not properly supplied by // the user) is not given. // - prints on stderr if an argument is missing and cmd_QUIET is NOT set. // - also has the side-effects of prompt_user() if we need to prompt // the user for input. // // ^RETURN-VALUE: // The current value of the (possibly modified) command status. This is a // combination of bitmasks of type cmdline_flags_t defined in <cmdline.h> // // ^ALGORITHM: // Foreach argument in cmd // if argument is required and was not given // if required, prompt for the missing argument // if prompting was unsuccesful add ARG_MISSING to cmd-status // endif // else add ARG_MISSING to cmd-status // endif // endif // endfor // return the current cmd-status //-^^---- unsigned CmdLine::missing_args(void) { char buf[256]; CmdArgListListIter list_iter(cmd_args); for (CmdArgList * alist = list_iter() ; alist ; alist = list_iter()) { CmdArgListIter iter(alist); for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) { if (cmdarg->is_dummy()) continue; if ((cmdarg->syntax() & CmdArg::isREQ) && (! (cmdarg->flags() & CmdArg::GIVEN))) { if (! (cmd_flags & QUIET)) { fmt_arg(cmdarg, buf, sizeof(buf), syntax(), TERSE_USAGE); error() << buf << " required." << endl ; } if (cmd_status & ARG_MISSING) { // user didnt supply the missing argument return cmd_status ; } else if ((! (cmd_flags & NO_ABORT)) && cmd_status) { // other problems return cmd_status ; } else if (cmd_flags & PROMPT_USER) { cmd_status |= prompt_user(cmdarg); } else { char * env = ::getenv("PROMPT_USER"); if (env && *env) { cmd_status |= prompt_user(cmdarg); } else { cmd_status |= ARG_MISSING ; } } } //if } //for iter } //for list_iter return cmd_status ; }
//------- // ^FUNCTION: CmdLine::kwd_match - purpose // // ^SYNOPSIS: // extern CmdArg * CmdLine::kwd_match(kwd, len, is_ambiguous, match_value); // // ^PARAMETERS: // const char * kwd; // -- a possible kewyord of "cmd" // // int len; // -- the number of character of "kwd" to consider (< 0 if all characters // of "kwd" should be used). // // int & is_ambiguous; // -- upon return, the value pointed to is set to 1 if the keyword // matches more than 1 keyword in "cmd"; Otherwise it is set to 0. // // int match_value; // -- if this is non-zero, then if a keyword_name is NULL use the // value_name instead. // // ^DESCRIPTION: // If "cmd" has an argument that matches "kwd" as a kewyord // then this function will find and return that argument. // // ^REQUIREMENTS: // // ^SIDE-EFFECTS: // None. // // ^RETURN-VALUE: // If we find a match, then we return a pointer to its argdesc, // otherwise we return NULL. // // ^ALGORITHM: // Set is_ambigous to 0. // For each argument in cmd // if argument's keyword-name matches kwd then // if this was an exact match then return this argument // else if we had a previous partial match of this argument then // if argument is a default argument return the previous match // else set is_ambiguous to 1 and return NULL // else remember we had a partial match here and keep trying // endif // endif // end for // if we has a partial match and we get to here then it is NOT ambiguous do // go ahead and return the argument we matched. //-^^---- CmdArg * CmdLine::kwd_match(const char * kwd, int len, int & is_ambiguous, int match_value) const { CmdArg * matched = NULL; is_ambiguous = 0 ; CmdArgListListIter list_iter(cmd_args); for (CmdArgList * alist = list_iter() ; alist ; alist = list_iter()) { CmdArgListIter iter(alist); for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) { if (cmdarg->is_dummy()) continue; // attempt to match this keyword strmatch_t result = str_NONE ; const char * source = cmdarg->keyword_name(); if (source && *source) { result = strmatch(source, kwd, len) ; } else if (match_value) { result = strmatch(cmdarg->value_name(), kwd, len) ; } if (result == str_EXACT) { return cmdarg ; } else if (result == str_PARTIAL) { if (matched) { is_ambiguous = 1 ; return NULL ; // ambiguous keyword } matched = cmdarg ; // we matched this one } } //for iter if (matched) break; } //for list_iter return matched ; }
int GetDirectory(CmdArg& szDir) { int iRc = 0; DWORD nLen, nMax; nMax = GetCurrentDirectory(MAX_PATH, szDir.GetBuffer(MAX_PATH)); if (!nMax) { goto wrap; } else if (nMax > MAX_PATH) { nLen = GetCurrentDirectory(nMax, szDir.GetBuffer(nMax)); if (!nLen || (nLen > nMax)) { goto wrap; } } iRc = lstrlen(szDir); wrap: return iRc; }
size_t CDefTermHk::GetSrvAddArgs(bool bGuiArgs, CmdArg& rsArgs, CmdArg& rsNewCon) { rsArgs.Empty(); rsNewCon.Empty(); if (!this) return 0; size_t cchMax = 64 + ((m_Opt.pszConfigName && *m_Opt.pszConfigName) ? lstrlen(m_Opt.pszConfigName) : 0); wchar_t* psz = rsArgs.GetBuffer(cchMax); size_t cchNew = 32; // "-new_console:ni" wchar_t* pszNew = rsNewCon.GetBuffer(cchNew); if (!psz || !pszNew) return 0; *psz = 0; *pszNew = 0; wchar_t szNewConSw[10] = L""; // Do not inject ConEmuHk in the target process? if (m_Opt.bNoInjects && !bGuiArgs) _wcscat_c(psz, cchMax, L" /NOINJECT"); else if (m_Opt.bNoInjects) wcscat_c(szNewConSw, L"i"); // New or existing window we shall use? if (m_Opt.bNewWindow && !bGuiArgs) _wcscat_c(psz, cchMax, L" /GHWND=NEW"); else if (m_Opt.bNewWindow) _wcscat_c(psz, cchMax, L" /NOSINGLE"); else if (bGuiArgs) _wcscat_c(psz, cchMax, L" /REUSE"); // Confirmations if (m_Opt.nDefaultTerminalConfirmClose == 1) { if (!bGuiArgs) _wcscat_c(psz, cchMax, L" /CONFIRM"); else wcscat_c(szNewConSw, L"c"); } else if (m_Opt.nDefaultTerminalConfirmClose == 2) { if (!bGuiArgs) _wcscat_c(psz, cchMax, L" /NOCONFIRM"); else wcscat_c(szNewConSw, L"n"); } // That switch must be processed in server too! if (m_Opt.pszConfigName && *m_Opt.pszConfigName) { _wcscat_c(psz, cchMax, L" /CONFIG \""); _wcscat_c(psz, cchMax, m_Opt.pszConfigName); _wcscat_c(psz, cchMax, L"\""); } if (*szNewConSw) { _wcscpy_c(pszNew, cchNew, L"-new_console:"); _wcscat_c(pszNew, cchNew, szNewConSw); _wcscat_c(pszNew, cchNew, L" "); } size_t cchLen = wcslen(psz) + wcslen(pszNew); return cchLen; }
bool IsNeedCmd(BOOL bRootCmd, LPCWSTR asCmdLine, CmdArg &szExe, LPCWSTR* rsArguments /*= NULL*/, BOOL* rpbNeedCutStartEndQuot /*= NULL*/, BOOL* rpbRootIsCmdExe /*= NULL*/, BOOL* rpbAlwaysConfirmExit /*= NULL*/, BOOL* rpbAutoDisableConfirmExit /*= NULL*/) { _ASSERTE(asCmdLine && *asCmdLine); bool rbNeedCutStartEndQuot = false; bool rbRootIsCmdExe = true; bool rbAlwaysConfirmExit = false; bool rbAutoDisableConfirmExit = false; if (rsArguments) *rsArguments = NULL; bool lbRc = false; int iRc = 0; BOOL lbFirstWasGot = FALSE; LPCWSTR pwszCopy; int nLastChar; #ifdef _DEBUG // Это минимальные проверки, собственно к коду - не относятся CmdArg szDbgFirst; bool bIsBatch = false; { LPCWSTR psz = asCmdLine; NextArg(&psz, szDbgFirst); 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:\" LPCWSTR pwszTemp = pwszCopy; // Получим первую команду (исполняемый файл?) if ((iRc = NextArg(&pwszTemp, szExe)) != 0) { //Parsing command line failed #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } 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(); // 17.10.2010 - поддержка переданного исполняемого файла без параметров, но с пробелами в пути LPCWSTR pchEnd = pwszCopy + lstrlenW(pwszCopy); while (pchEnd > pwszCopy && *(pchEnd-1) == L' ') pchEnd--; if ((pchEnd - pwszCopy) < MAX_PATH) { szExe.Set(pwszCopy, (pchEnd - pwszCopy)); _ASSERTE(szExe[(pchEnd - pwszCopy)] == 0); if (lstrcmpiW(szExe, L"start") == 0) { // Команду start обрабатывает только процессор #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } // Обработка переменных окружения и поиск в PATH if (FileExistsSearch((LPCWSTR)szExe, szExe)) { if (rsArguments) *rsArguments = pchEnd; } else { szExe.Empty(); } } if (szExe[0] == 0) { if ((iRc = NextArg(&pwszCopy, szExe)) != 0) { //Parsing command line failed #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } if (lstrcmpiW(szExe, L"start") == 0) { // Команду start обрабатывает только процессор #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } // Обработка переменных окружения и поиск в 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" } } // Пробуем найти "по путям" соответствующий exe-шник. DWORD nCchMax = szExe.mn_MaxLen; // выделить память, длинее чем szExe вернуть не сможем wchar_t* pszSearch = (wchar_t*)malloc(nCchMax*sizeof(wchar_t)); if (pszSearch) { #ifndef CONEMU_MINIMAL MWow64Disable wow; wow.Disable(); // Отключить редиректор! #endif wchar_t *pszName = NULL; DWORD nRc = SearchPath(NULL, szExe, bHasExt ? NULL : L".exe", nCchMax, pszSearch, &pszName); if (nRc && (nRc < nCchMax)) { // Нашли, возвращаем что нашли szExe.Set(pszSearch); } free(pszSearch); } } // 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 wchar_t *pwszEndSpace = szExe.ms_Arg + 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) { wchar_t szSearch[MAX_PATH+1], *pszPart = NULL; DWORD n = SearchPath(NULL, pwszCopy, L".exe", countof(szSearch), szSearch, &pszPart); if (n && (n < countof(szSearch))) { if (lstrcmpi(PointToExt(pszPart), 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; }
//------- // ^FUNCTION: CmdLine::print_synopsis // // ^SYNOPSIS: // unsigned CmdLine::print_synopsis(syntax, os, cols) // // ^PARAMETERS: // CmdLine::CmdLineSyntax syntax; // -- the syntax to use (long-option, short-option, or both) // when printing the synopsis. // // ostream & os; // -- where to print. // // int cols; // -- the maximum width of a line. // // ^DESCRIPTION: // Print a command-line synopsis (the command-line syntax). // The synopsis should be printed to "os" using the desired syntax, // in lines that are no more than "cols" characters wide. // // ^REQUIREMENTS: // // ^SIDE-EFFECTS: // Prints on "os". // // ^RETURN-VALUE: // The length of the longest argument-buf that was printed. // // ^ALGORITHM: // It's kind of complicated so follow along! //-^^---- unsigned CmdLine::print_synopsis(CmdLine::CmdLineSyntax syntax, ostream & os, int cols) const { #ifdef vms_style static char usg_prefix[] = "Format: "; #else static char usg_prefix[] = "Usage: "; #endif unsigned ll, positionals, longest = 0; // first print the command name os << usg_prefix << cmd_name ; ll = (cmd_name ? ::strlen(cmd_name) : 0) + (sizeof(usg_prefix) - 1); // set margin so that we always start printing arguments in a column // that is *past* the command name. // unsigned margin = ll + 1; // print option-syntax followed by positional parameters int first; char buf[256] ; for (positionals = 0 ; positionals < 2 ; positionals++) { first = 1; CmdArgListListIter list_iter(cmd_args); for (CmdArgList * alist = list_iter() ; alist ; alist = list_iter()) { CmdArgListIter iter(alist); for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) { unsigned len, pl; // don't display hidden arguments if (cmdarg->syntax() & CmdArg::isHIDDEN) continue; if (!positionals && (cmdarg->syntax() & CmdArg::isPOS)) continue; if (positionals && !(cmdarg->syntax() & CmdArg::isPOS)) continue; // figure out how wide this parameter is (for printing) pl = len = fmt_arg(cmdarg, buf, sizeof(buf), syntax, VERBOSE_USAGE); if (! len) continue; if (cmdarg->syntax() & CmdArg::isLIST) pl -= 4 ; // " ..." if (! (cmdarg->syntax() & CmdArg::isREQ)) pl -= 2 ; // "[]" if (pl > longest) longest = pl; // Will this fit? if ((ll + len + 1) > (cols - first)) { os << char('\n') ; os.width(margin); os << "" ; // No - start a new line; ll = margin; } else { os << char(' ') ; // Yes - just throw in a space ++ll; } ll += len; os << buf; first = 0; } //for each cmdarg } //for each arg-list } //for each parm-type os << endl ; return longest ; }
// Returns true, if application was found in registry: // [HKCU|HKLM]\Software\Microsoft\Windows\CurrentVersion\App Paths // Also, function may change local process %PATH% variable bool SearchAppPaths(LPCWSTR asFilePath, CmdArg& rsFound, bool abSetPath, CmdArg* rpsPathRestore /*= NULL*/) { if (rpsPathRestore) rpsPathRestore->Empty(); if (!asFilePath || !*asFilePath) return false; LPCWSTR pszSearchFile = PointToName(asFilePath); LPCWSTR pszExt = PointToExt(pszSearchFile); // Lets try find it in "App Paths" // "HKCU\Software\Microsoft\Windows\CurrentVersion\App Paths\" // "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\" LPCWSTR pszRoot = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"; HKEY hk; LONG lRc; CmdArg lsName; lsName.Attach(lstrmerge(pszRoot, pszSearchFile, pszExt ? NULL : L".exe")); // Seems like 32-bit and 64-bit registry branches are the same now, but just in case - will check both DWORD nWOW[2] = {WIN3264TEST(KEY_WOW64_32KEY,KEY_WOW64_64KEY), WIN3264TEST(KEY_WOW64_64KEY,KEY_WOW64_32KEY)}; for (int i = 0; i < 3; i++) { bool bFound = false; DWORD nFlags = ((i && IsWindows64()) ? nWOW[i-1] : 0); if ((i == 2) && !nFlags) break; // This is 32-bit OS lRc = RegOpenKeyEx(i ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, lsName, 0, KEY_READ|nFlags, &hk); if (lRc != 0) continue; wchar_t szVal[MAX_PATH+1] = L""; DWORD nType, nSize = sizeof(szVal)-sizeof(szVal[0]); lRc = RegQueryValueEx(hk, NULL, NULL, &nType, (LPBYTE)szVal, &nSize); if (lRc == 0) { wchar_t *pszCheck = NULL; if (nType == REG_SZ) { pszCheck = szVal; } else if (nType == REG_EXPAND_SZ) { pszCheck = ExpandEnvStr(szVal); } // May be quoted if (pszCheck) { LPCWSTR pszPath = Unquote(pszCheck, true); if (FileExists(pszPath)) { // asFilePath will be invalid after .Set rsFound.Set(pszPath); bFound = true; if (pszCheck != szVal) free(pszCheck); // The program may require additional "%PATH%". So, if allowed... if (abSetPath) { nSize = 0; lRc = RegQueryValueEx(hk, L"PATH", NULL, &nType, NULL, &nSize); if (lRc == 0 && nSize) { wchar_t* pszCurPath = GetEnvVar(L"PATH"); wchar_t* pszAddPath = (wchar_t*)calloc(nSize+4,1); wchar_t* pszNewPath = NULL; if (pszAddPath) { lRc = RegQueryValueEx(hk, L"PATH", NULL, &nType, (LPBYTE)pszAddPath, &nSize); if (lRc == 0 && *pszAddPath) { // Если в "%PATH%" этого нет (в начале) - принудительно добавить int iCurLen = pszCurPath ? lstrlen(pszCurPath) : 0; int iAddLen = lstrlen(pszAddPath); bool bNeedAdd = true; if ((iCurLen >= iAddLen) && (pszCurPath[iAddLen] == L';' || pszCurPath[iAddLen] == 0)) { wchar_t ch = pszCurPath[iAddLen]; pszCurPath[iAddLen] = 0; if (lstrcmpi(pszCurPath, pszAddPath) == 0) bNeedAdd = false; pszCurPath[iAddLen] = ch; } // Если пути еще нет if (bNeedAdd) { if (rpsPathRestore) { rpsPathRestore->SavePathVar(pszCurPath); } pszNewPath = lstrmerge(pszAddPath, L";", pszCurPath); if (pszNewPath) { SetEnvironmentVariable(L"PATH", pszNewPath); } else { _ASSERTE(pszNewPath!=NULL && "Allocation failed?"); } } } } SafeFree(pszAddPath); SafeFree(pszCurPath); SafeFree(pszNewPath); } } } } } RegCloseKey(hk); if (bFound) return true; } return false; }
void RConStartArgs::RunArgTests() { struct { LPCWSTR pszArg, pszNeed; } cTests[] = { { L"\"c:\\cmd.exe\" \"-new_console\" \"c:\\file.txt\"", L"\"c:\\cmd.exe\" \"c:\\file.txt\"" }, { L"\"c:\\cmd.exe\" -new_console:n \"c:\\file.txt\"", L"\"c:\\cmd.exe\" \"c:\\file.txt\"" }, { L"\"c:\\cmd.exe\" \"-new_console:n\" \"c:\\file.txt\"", L"\"c:\\cmd.exe\" \"c:\\file.txt\"" }, { L"c:\\cmd.exe \"-new_console:n\" \"c:\\file.txt\"", L"c:\\cmd.exe \"c:\\file.txt\"" }, { L"\"c:\\cmd.exe\" \"-new_console:n\" c:\\file.txt", L"\"c:\\cmd.exe\" c:\\file.txt" }, { L"c:\\file.txt -cur_console", L"c:\\file.txt" }, { L"\"c:\\file.txt\" -cur_console", L"\"c:\\file.txt\"" }, { L" -cur_console \"c:\\file.txt\"", L" \"c:\\file.txt\"" }, { L"-cur_console \"c:\\file.txt\"", L"\"c:\\file.txt\"" }, { L"-cur_console c:\\file.txt", L"c:\\file.txt" }, }; for (size_t i = 0; i < countof(cTests); i++) { RConStartArgs arg; arg.pszSpecialCmd = lstrdup(cTests[i].pszArg); arg.ProcessNewConArg(); if (lstrcmp(arg.pszSpecialCmd, cTests[i].pszNeed) != 0) { //_ASSERTE(FALSE && "arg.ProcessNewConArg failed"); OutputDebugString(L"arg.ProcessNewConArg failed\n"); } int nDbg = 0; } for (size_t i = 0; i <= 4; i++) { RConStartArgs arg; switch (i) { case 0: arg.pszSpecialCmd = lstrdup(L"cmd \"-new_console:d:C:\\John Doe\\Home\" "); break; case 1: arg.pszSpecialCmd = lstrdup(L"cmd -cur_console:u"); break; case 2: arg.pszSpecialCmd = lstrdup(L"cmd -cur_console:u:Max"); break; case 3: arg.pszSpecialCmd = lstrdup(L"cmd -cur_console:u:Max:"); break; case 4: arg.pszSpecialCmd = lstrdup(L"cmd \"-new_console:P:^<Power\"\"Shell^>\""); break; } arg.ProcessNewConArg(); int nDbg = lstrcmp(arg.pszSpecialCmd, i ? L"cmd" : L"cmd "); _ASSERTE(nDbg==0); switch (i) { case 0: nDbg = lstrcmp(arg.pszStartupDir, L"C:\\John Doe\\Home"); _ASSERTE(nDbg==0); break; case 1: _ASSERTE(arg.pszUserName==NULL && arg.pszDomain==NULL && arg.bForceUserDialog); break; case 2: nDbg = lstrcmp(arg.pszUserName,L"Max"); _ASSERTE(nDbg==0 && arg.pszDomain==NULL && !*arg.szUserPassword && arg.bForceUserDialog); break; case 3: nDbg = lstrcmp(arg.pszUserName,L"Max"); _ASSERTE(nDbg==0 && arg.pszDomain==NULL && !*arg.szUserPassword && !arg.bForceUserDialog); break; case 4: nDbg = lstrcmp(arg.pszPalette, L"<Power\"Shell>"); _ASSERTE(nDbg==0); break; } } CmdArg s; s.Set(L"Abcdef", 3); int nDbg = lstrcmp(s, L"Abc"); _ASSERTE(nDbg==0); s.Set(L"qwerty"); nDbg = lstrcmp(s, L"qwerty"); _ASSERTE(nDbg==0); s.Empty(); //s.Set(L""); // !! Set("") must trigger ASSERT !! nDbg = s.ms_Arg ? lstrcmp(s, L"") : -2; _ASSERTE(nDbg==0); struct { LPCWSTR pszWhole; LPCWSTR pszCmp[5]; } lsArgTest[] = { {L"\"This is test\" Next-arg \t\n \"Third Arg +++++++++++++++++++\" ++", {L"This is test", L"Next-arg", L"Third Arg +++++++++++++++++++"}}, {L"\"\"cmd\"\"", {L"cmd"}}, {L"\"\"c:\\Windows\\System32\\cmd.exe\" /?\"", {L"c:\\Windows\\System32\\cmd.exe", L"/?"}}, // Following example is crazy, but quotation issues may happens //{L"First Sec\"\"ond \"Thi\"rd\" \"Fo\"\"rth\"", {L"First", L"Sec\"\"ond", L"Thi\"rd", L"Fo\"\"rth"}}, {L"First \"Fo\"\"rth\"", {L"First", L"Fo\"\"rth"}}, // Multiple commands {L"set ConEmuReportExe=VIM.EXE & SH.EXE", {L"set", L"ConEmuReportExe=VIM.EXE", L"&", L"SH.EXE"}}, {NULL} }; for (int i = 0; lsArgTest[i].pszWhole; i++) { s.Empty(); LPCWSTR pszTestCmd = lsArgTest[i].pszWhole; int j = -1; while (lsArgTest[i].pszCmp[++j]) { if (NextArg(&pszTestCmd, s) != 0) { _ASSERTE(FALSE && "Fails on token!"); } else { nDbg = lstrcmp(s, lsArgTest[i].pszCmp[j]); _ASSERTE(nDbg==0); } } } nDbg = -1; }
LPCWSTR CRConFiles::GetFileFromConsole(LPCWSTR asSrc, CmdArg& szFull) { CmdArg szWinPath; LPCWSTR pszWinPath = szWinPath.Attach(MakeWinPath(asSrc)); if (!pszWinPath || !*pszWinPath) { _ASSERTE(pszWinPath && *pszWinPath); return NULL; } if (IsFilePath(pszWinPath, true)) { if (!FileExists(pszWinPath)) // otherwise it will cover directories too return NULL; szFull.Attach(szWinPath.Detach()); } else { CEStr szDir; LPCWSTR pszDir = mp_RCon->GetConsoleCurDir(szDir); _ASSERTE(pszDir && wcschr(pszDir,L'/')==NULL); // Попытаться просканировать один-два уровеня подпапок bool bFound = FileExistSubDir(pszDir, pszWinPath, 1, szFull); if (!bFound) { // git diffs style, but with backslashes (they are converted already) // "a/src/ConEmu.cpp" and "b/src/ConEmu.cpp" if (pszWinPath[0] && (pszWinPath[1] == L'\\') && wcschr(L"abicwo", pszWinPath[0])) { LPCWSTR pszSlash = pszWinPath; while (!bFound && ((pszSlash = wcschr(pszSlash, L'\\')) != NULL)) { while (*pszSlash == L'\\') pszSlash++; if (!*pszSlash) break; bFound = FileExistSubDir(pszDir, pszSlash, 1, szFull); } } } if (!bFound) { // If there is "src" subfolder in the current folder CEStr szSrc = JoinPath(pszDir, L"src"); if (DirectoryExists(szSrc)) { bFound = FileExistSubDir(szSrc, pszWinPath, 1, szFull); } } if (!bFound) { return NULL; } } if (!szFull.IsEmpty()) { // "src\conemu\realconsole.cpp" --> "src\ConEmu\RealConsole.cpp" MakePathProperCase(szFull); } return szFull; }
void RConStartArgs::RunArgTests() { CmdArg s; s.Set(L"Abcdef", 3); int nDbg = lstrcmp(s, L"Abc"); _ASSERTE(nDbg==0); s.Set(L"qwerty"); nDbg = lstrcmp(s, L"qwerty"); _ASSERTE(nDbg==0); s.Empty(); //s.Set(L""); // !! Set("") must trigger ASSERT !! nDbg = s.ms_Arg ? lstrcmp(s, L"") : -2; _ASSERTE(nDbg==0); struct { LPCWSTR pszWhole; LPCWSTR pszCmp[10]; } lsArgTest[] = { {L"\"C:\\ConEmu\\ConEmuC64.exe\" /PARENTFARPID=1 /C \"C:\\GIT\\cmdw\\ad.cmd CE12.sln & ci -m \"Solution debug build properties\"\"", {L"C:\\ConEmu\\ConEmuC64.exe", L"/PARENTFARPID=1", L"/C", L"C:\\GIT\\cmdw\\ad.cmd", L"CE12.sln", L"&", L"ci", L"-m", L"Solution debug build properties"}}, {L"/C \"C:\\ad.cmd file.txt & ci -m \"Commit message\"\"", {L"/C", L"C:\\ad.cmd", L"file.txt", L"&", L"ci", L"-m", L"Commit message"}}, {L"\"This is test\" Next-arg \t\n \"Third Arg +++++++++++++++++++\" ++", {L"This is test", L"Next-arg", L"Third Arg +++++++++++++++++++"}}, {L"\"\"cmd\"\"", {L"cmd"}}, {L"\"\"c:\\Windows\\System32\\cmd.exe\" /?\"", {L"c:\\Windows\\System32\\cmd.exe", L"/?"}}, // Following example is crazy, but quotation issues may happens //{L"First Sec\"\"ond \"Thi\"rd\" \"Fo\"\"rth\"", {L"First", L"Sec\"\"ond", L"Thi\"rd", L"Fo\"\"rth"}}, {L"First \"Fo\"\"rth\"", {L"First", L"Fo\"\"rth"}}, // Multiple commands {L"set ConEmuReportExe=VIM.EXE & SH.EXE", {L"set", L"ConEmuReportExe=VIM.EXE", L"&", L"SH.EXE"}}, // Inside escaped arguments {L"reg.exe add \"HKCU\\MyCo\" /ve /t REG_EXPAND_SZ /d \"\\\"C:\\ConEmu\\ConEmuPortable.exe\\\" /Dir \\\"%V\\\" /cmd \\\"cmd.exe\\\" \\\"-new_console:nC:cmd.exe\\\" \\\"-cur_console:d:%V\\\"\" /f", // Для наглядности: // reg.exe add "HKCU\MyCo" /ve /t REG_EXPAND_SZ // /d "\"C:\ConEmu\ConEmuPortable.exe\" /Dir \"%V\" /cmd \"cmd.exe\" \"-new_console:nC:cmd.exe\" \"-cur_console:d:%V\"" /f {L"reg.exe", L"add", L"HKCU\\MyCo", L"/ve", L"/t", L"REG_EXPAND_SZ", L"/d", L"\\\"C:\\ConEmu\\ConEmuPortable.exe\\\" /Dir \\\"%V\\\" /cmd \\\"cmd.exe\\\" \\\"-new_console:nC:cmd.exe\\\" \\\"-cur_console:d:%V\\\"", L"/f"}}, // After 'Inside escaped arguments' regression bug appears {L"/dir \"C:\\\" /icon \"cmd.exe\" /single", {L"/dir", L"C:\\", L"/icon", L"cmd.exe", L"/single"}}, {L"cmd \"one.exe /dir \\\"C:\\\\\" /log\" \"two.exe /dir \\\"C:\\\" /log\" end", {L"cmd", L"one.exe /dir \\\"C:\\\\\" /log", L"two.exe /dir \\\"C:\\\" /log", L"end"}}, {NULL} }; for (int i = 0; lsArgTest[i].pszWhole; i++) { s.Empty(); LPCWSTR pszTestCmd = lsArgTest[i].pszWhole; int j = -1; while (lsArgTest[i].pszCmp[++j]) { if (NextArg(&pszTestCmd, s) != 0) { _ASSERTE(FALSE && "Fails on token!"); } else { nDbg = lstrcmp(s, lsArgTest[i].pszCmp[j]); _ASSERTE(nDbg==0); } } } bool bTest = true; for (size_t i = 0; bTest; i++) { RConStartArgs arg; int nDbg; LPCWSTR pszCmp; switch (i) { case 21: pszCmp = L"cmd '-new_console' `-new_console` \\\"-new_console\\\""; arg.pszSpecialCmd = lstrdup(pszCmp); arg.ProcessNewConArg(); _ASSERTE(0==lstrcmp(arg.pszSpecialCmd, pszCmp) && arg.NewConsole==crb_Undefined); break; case 20: arg.pszSpecialCmd = lstrdup(L"\"c:\\cmd.exe\" \"-new_console\" \"c:\\file.txt\""); arg.ProcessNewConArg(); _ASSERTE(0==lstrcmp(arg.pszSpecialCmd, L"\"c:\\cmd.exe\" \"c:\\file.txt\"")); break; case 19: arg.pszSpecialCmd = lstrdup(L"\"c:\\cmd.exe\" -new_console:n \"c:\\file.txt\""); arg.ProcessNewConArg(); _ASSERTE(0==lstrcmp(arg.pszSpecialCmd, L"\"c:\\cmd.exe\" \"c:\\file.txt\"")); break; case 18: arg.pszSpecialCmd = lstrdup(L"\"c:\\cmd.exe\" \"-new_console:n\" \"c:\\file.txt\""); arg.ProcessNewConArg(); _ASSERTE(0==lstrcmp(arg.pszSpecialCmd, L"\"c:\\cmd.exe\" \"c:\\file.txt\"")); break; case 17: arg.pszSpecialCmd = lstrdup(L"c:\\cmd.exe \"-new_console:n\" \"c:\\file.txt\""); arg.ProcessNewConArg(); _ASSERTE(0==lstrcmp(arg.pszSpecialCmd, L"c:\\cmd.exe \"c:\\file.txt\"")); break; case 16: arg.pszSpecialCmd = lstrdup(L"\"c:\\cmd.exe\" \"-new_console:n\" c:\\file.txt"); arg.ProcessNewConArg(); _ASSERTE(0==lstrcmp(arg.pszSpecialCmd, L"\"c:\\cmd.exe\" c:\\file.txt")); break; case 15: arg.pszSpecialCmd = lstrdup(L"c:\\file.txt -cur_console"); arg.ProcessNewConArg(); _ASSERTE(0==lstrcmp(arg.pszSpecialCmd, L"c:\\file.txt")); break; case 14: arg.pszSpecialCmd = lstrdup(L"\"c:\\file.txt\" -cur_console"); arg.ProcessNewConArg(); _ASSERTE(0==lstrcmp(arg.pszSpecialCmd, L"\"c:\\file.txt\"")); break; case 13: arg.pszSpecialCmd = lstrdup(L" -cur_console \"c:\\file.txt\""); arg.ProcessNewConArg(); _ASSERTE(0==lstrcmp(arg.pszSpecialCmd, L"\"c:\\file.txt\"")); break; case 12: arg.pszSpecialCmd = lstrdup(L"-cur_console \"c:\\file.txt\""); arg.ProcessNewConArg(); _ASSERTE(0==lstrcmp(arg.pszSpecialCmd, L"\"c:\\file.txt\"")); break; case 11: arg.pszSpecialCmd = lstrdup(L"-cur_console c:\\file.txt"); arg.ProcessNewConArg(); _ASSERTE(0==lstrcmp(arg.pszSpecialCmd, L"c:\\file.txt")); break; case 10: pszCmp = L"reg.exe add \"HKCU\\command\" /ve /t REG_EXPAND_SZ /d \"\\\"C:\\ConEmu\\ConEmuPortable.exe\\\" /Dir \\\"%V\\\" /cmd \\\"cmd.exe\\\" \\\"-new_console:nC:cmd.exe\\\" \\\"-cur_console:d:%V\\\"\" /f"; arg.pszSpecialCmd = lstrdup(pszCmp); arg.ProcessNewConArg(); _ASSERTE(lstrcmp(arg.pszSpecialCmd, pszCmp)==0 && arg.NewConsole==crb_Undefined); break; case 9: pszCmp = L"\"C:\\Windows\\system32\\cmd.exe\" /C \"\"C:\\Python27\\python.EXE\"\""; arg.pszSpecialCmd = lstrdup(pszCmp); arg.ProcessNewConArg(); _ASSERTE(lstrcmp(arg.pszSpecialCmd, pszCmp)==0); break; case 8: arg.pszSpecialCmd = lstrdup(L"cmd --new_console -cur_console:a"); arg.ProcessNewConArg(); _ASSERTE(lstrcmp(arg.pszSpecialCmd, L"cmd --new_console")==0 && arg.NewConsole==crb_Undefined && arg.RunAsAdministrator==crb_On); break; case 7: arg.pszSpecialCmd = lstrdup(L"cmd -cur_console:d:\"C:\\My docs\":t:\"My title\" \"-cur_console:C:C:\\my cmd.ico\" -cur_console:P:\"<PowerShell>\":a /k ver"); arg.ProcessNewConArg(); pszCmp = L"cmd /k ver"; _ASSERTE(lstrcmp(arg.pszSpecialCmd, pszCmp)==0); _ASSERTE(arg.pszRenameTab && arg.pszPalette && arg.pszIconFile && arg.pszStartupDir && arg.NewConsole==crb_Undefined && lstrcmp(arg.pszRenameTab, L"My title")==0 && lstrcmp(arg.pszPalette, L"<PowerShell>")==0 && lstrcmp(arg.pszStartupDir, L"C:\\My docs")==0 && lstrcmp(arg.pszIconFile, L"C:\\my cmd.ico")==0); break; case 6: arg.pszSpecialCmd = lstrdup(L"cmd -cur_console:b:P:\"^<Power\"\"Shell^>\":t:\"My title\" /k ConEmuC.exe -Guimacro print(\"-new_console:a\")"); arg.ProcessNewConArg(); pszCmp = L"cmd /k ConEmuC.exe -Guimacro print(\"-new_console:a\")"; _ASSERTE(lstrcmp(arg.pszSpecialCmd, pszCmp)==0); _ASSERTE(arg.pszRenameTab && arg.pszPalette && arg.BackgroundTab==crb_On && arg.NewConsole==crb_Undefined && arg.RunAsAdministrator==crb_Undefined && lstrcmp(arg.pszRenameTab, L"My title")==0 && lstrcmp(arg.pszPalette, L"<Power\"Shell>")==0); break; case 5: arg.pszSpecialCmd = lstrdup(L"cmd \"-cur_console:t:My title\" /k ver"); arg.ProcessNewConArg(); _ASSERTE(lstrcmp(arg.pszSpecialCmd, L"cmd /k ver")==0); _ASSERTE(arg.pszRenameTab && lstrcmp(arg.pszRenameTab, L"My title")==0 && arg.NewConsole==crb_Undefined); break; case 4: arg.pszSpecialCmd = lstrdup(L"cmd \"-new_console:P:^<Power\"\"Shell^>\""); arg.ProcessNewConArg(); _ASSERTE(lstrcmp(arg.pszSpecialCmd, L"cmd")==0); nDbg = lstrcmp(arg.pszPalette, L"<Power\"Shell>"); _ASSERTE(nDbg==0 && arg.NewConsole==crb_On); break; case 3: arg.pszSpecialCmd = lstrdup(L"cmd -cur_console:u:Max:"); arg.ProcessNewConArg(); _ASSERTE(lstrcmp(arg.pszSpecialCmd, L"cmd")==0); nDbg = lstrcmp(arg.pszUserName,L"Max"); _ASSERTE(nDbg==0 && arg.pszDomain==NULL && !*arg.szUserPassword && arg.ForceUserDialog==crb_Off && arg.NewConsole!=crb_On); break; case 2: arg.pszSpecialCmd = lstrdup(L"cmd -cur_console:u:Max -new_console"); arg.ProcessNewConArg(); _ASSERTE(lstrcmp(arg.pszSpecialCmd, L"cmd")==0); nDbg = lstrcmp(arg.pszUserName,L"Max"); _ASSERTE(nDbg==0 && arg.pszDomain==NULL && !*arg.szUserPassword && arg.ForceUserDialog==crb_On && arg.NewConsole==crb_On); break; case 1: arg.pszSpecialCmd = lstrdup(L"cmd -new_console:u -cur_console:h0"); arg.ProcessNewConArg(); _ASSERTE(lstrcmp(arg.pszSpecialCmd, L"cmd")==0); _ASSERTE(arg.pszUserName==NULL && arg.pszDomain==NULL && arg.ForceUserDialog==crb_On && arg.NewConsole==crb_On && arg.BufHeight==crb_On && arg.nBufHeight==0); break; case 0: arg.pszSpecialCmd = lstrdup(L"cmd \"-new_console:d:C:\\John Doe\\Home\" "); arg.ProcessNewConArg(); _ASSERTE(lstrcmp(arg.pszSpecialCmd, L"cmd ")==0); nDbg = lstrcmp(arg.pszStartupDir, L"C:\\John Doe\\Home"); _ASSERTE(nDbg==0 && arg.NewConsole==crb_On); break; default: bTest = false; // Stop tests } } nDbg = -1; }
// Function checks, if we need drop first and last quotation marks // Example: ""7z.exe" /?" // Using cmd.exe rules bool IsNeedDequote(LPCWSTR asCmdLine, bool abFromCmdCK, LPCWSTR* rsEndQuote/*=NULL*/) { if (rsEndQuote) *rsEndQuote = NULL; if (!asCmdLine) return false; bool bDeQu = false; LPCWSTR pszQE, pszSP; if (asCmdLine[0] == L'"') { bDeQu = (asCmdLine[1] == L'"'); // Всегда - нельзя. Иначе парсинг строки запуска некорректно идет // L"\"C:\\ConEmu\\ConEmuC64.exe\" /PARENTFARPID=1 /C \"C:\\GIT\\cmdw\\ad.cmd CE12.sln & ci -m \"Solution debug build properties\"\"" if (!bDeQu) { size_t nLen = lstrlen(asCmdLine); if (abFromCmdCK) { bDeQu = ((asCmdLine[nLen-1] == L'"') && (asCmdLine[nLen-2] == L'"')); } if (!bDeQu && (asCmdLine[nLen-1] == L'"')) { pszSP = wcschr(asCmdLine+1, L' '); pszQE = wcschr(asCmdLine+1, L'"'); if (pszSP && pszQE && (pszSP < pszQE) && ((pszSP - asCmdLine) < MAX_PATH)) { CmdArg lsTmp; lsTmp.Set(asCmdLine+1, pszSP-asCmdLine-1); bDeQu = (IsFilePath(lsTmp, true) && IsExecutable(lsTmp)); } } } } if (!bDeQu) return false; // Don't dequote? pszQE = wcsrchr(asCmdLine+2, L'"'); if (!pszQE) return false; #if 0 LPCWSTR pszQ1 = wcschr(asCmdLine+2, L'"'); if (!pszQ1) return false; LPCWSTR pszQE = wcsrchr(pszQ1, L'"'); // Only TWO quotes in asCmdLine? if (pszQE == pszQ1) { // Doesn't contains special symbols? if (!wcspbrk(asCmdLine+1, L"&<>()@^|")) { // Must contains spaces (doubt?) if (wcschr(asCmdLine+1, L' ')) { // Cmd also checks this for executable file name. Skip this check? return false; } } } #endif // Well, we get here _ASSERTE(asCmdLine[0]==L'"' && pszQE && *pszQE==L'"' && !wcschr(pszQE+1,L'"')); // Dequote it! if (rsEndQuote) *rsEndQuote = pszQE; return true; }
// Возвращает 0, если успешно, иначе - ошибка int NextArg(const wchar_t** asCmdLine, CmdArg &rsArg, const wchar_t** rsArgStart/*=NULL*/) { if (!asCmdLine || !*asCmdLine) return CERR_CMDLINEEMPTY; #ifdef _DEBUG if ((rsArg.mn_TokenNo==0) // first token || ((rsArg.mn_TokenNo>0) && (rsArg.ms_LastTokenEnd==*asCmdLine) && (wcsncmp(*asCmdLine,rsArg.ms_LastTokenSave,countof(rsArg.ms_LastTokenSave)-1))==0)) { // OK, параметры корректны } else { _ASSERTE(FALSE && "rsArgs was not resetted before new cycle!"); } #endif LPCWSTR psCmdLine = SkipNonPrintable(*asCmdLine), pch = NULL; if (!*psCmdLine) return CERR_CMDLINEEMPTY; // Remote surrounding quotes, in certain cases // Example: ""7z.exe" /?" // Example: "C:\Windows\system32\cmd.exe" /C ""C:\Python27\python.EXE"" if ((rsArg.mn_TokenNo == 0) || (rsArg.mn_CmdCall == CmdArg::cc_CmdCK)) { if (IsNeedDequote(psCmdLine, (rsArg.mn_CmdCall == CmdArg::cc_CmdCK), &rsArg.mpsz_Dequoted)) psCmdLine++; if (rsArg.mn_CmdCall == CmdArg::cc_CmdCK) rsArg.mn_CmdCall = CmdArg::cc_CmdCommand; } size_t nArgLen = 0; bool lbQMode = false; // аргумент начинается с " if (*psCmdLine == L'"') { lbQMode = true; psCmdLine++; // ... /d "\"C:\ConEmu\ConEmuPortable.exe\" /Dir ... bool bQuoteEscaped = (psCmdLine[0] == L'\\' && psCmdLine[1] == L'"'); pch = wcschr(psCmdLine, L'"'); if (pch && (pch > psCmdLine)) { // To be correctly parsed something like this: // reg.exe add "HKCU\MyCo" /ve /t REG_EXPAND_SZ /d "\"C:\ConEmu\ConEmuPortable.exe\" /Dir \"%V\" /cmd \"cmd.exe\" \"-new_console:nC:cmd.exe\" \"-cur_console:d:%V\"" /f // But must not fails with ‘simple’ command like (no escapes in "C:\"): // /dir "C:\" /icon "cmd.exe" /single //while (pch && (*(pch-1) == L'\\')) // pch = wcschr(pch+1, L'"'); pch = wcspbrk(psCmdLine, L"\\\""); while (pch) { // Escaped quotation? if ((*pch == L'\\') && (*(pch+1) == L'"')) { // It's allowed when: // a) at the beginning of the line (handled above, bQuoteEscaped); // b) after space; // c) when already was forced by bQuoteEscaped if ((((pch - 1) >= psCmdLine) && (*(pch-1) == L' ')) || bQuoteEscaped) { bQuoteEscaped = true; pch++; // Point to " } } else if (*pch == L'"') break; // Next entry AFTER pch pch = wcspbrk(pch+1, L"\\\""); } } if (!pch) return CERR_CMDLINE; while (pch[1] == L'"' && (!rsArg.mpsz_Dequoted || ((pch+1) < rsArg.mpsz_Dequoted))) { pch += 2; pch = wcschr(pch, L'"'); if (!pch) return CERR_CMDLINE; } // Теперь в pch ссылка на последнюю " } else { // До конца строки или до первого пробела //pch = wcschr(psCmdLine, L' '); // 09.06.2009 Maks - обломался на: cmd /c" echo Y " pch = psCmdLine; // Ищем обычным образом (до пробела/кавычки) while (*pch && *pch!=L' ' && *pch!=L'"') pch++; //if (!pch) pch = psCmdLine + lstrlenW(psCmdLine); // до конца строки } nArgLen = pch - psCmdLine; // Вернуть аргумент if (!rsArg.Set(psCmdLine, nArgLen)) return CERR_CMDLINE; rsArg.mn_TokenNo++; if (rsArgStart) *rsArgStart = psCmdLine; psCmdLine = pch; // Finalize if ((*psCmdLine == L'"') && (lbQMode || (rsArg.mpsz_Dequoted == psCmdLine))) psCmdLine++; // was pointed to closing quotation mark psCmdLine = SkipNonPrintable(psCmdLine); // When whole line was dequoted if ((*psCmdLine == L'"') && (rsArg.mpsz_Dequoted == psCmdLine)) psCmdLine++; #ifdef _DEBUG rsArg.ms_LastTokenEnd = psCmdLine; lstrcpyn(rsArg.ms_LastTokenSave, psCmdLine, countof(rsArg.ms_LastTokenSave)); #endif switch (rsArg.mn_CmdCall) { case CmdArg::cc_Undefined: // Если это однозначно "ключ" - то на имя файла не проверяем if (*rsArg.ms_Arg == L'/' || *rsArg.ms_Arg == L'-') { // Это для парсинга (чтобы ассертов не было) параметров из ShellExecute (там cmd.exe указывается в другом аргументе) if ((rsArg.mn_TokenNo == 1) && (lstrcmpi(rsArg.ms_Arg, L"/C") == 0 || lstrcmpi(rsArg.ms_Arg, L"/K") == 0)) rsArg.mn_CmdCall = CmdArg::cc_CmdCK; } else { pch = PointToName(rsArg.ms_Arg); if (pch) { if ((lstrcmpi(pch, L"cmd") == 0 || lstrcmpi(pch, L"cmd.exe") == 0) || (lstrcmpi(pch, L"ConEmuC") == 0 || lstrcmpi(pch, L"ConEmuC.exe") == 0) || (lstrcmpi(pch, L"ConEmuC64") == 0 || lstrcmpi(pch, L"ConEmuC64.exe") == 0)) { rsArg.mn_CmdCall = CmdArg::cc_CmdExeFound; } } } break; case CmdArg::cc_CmdExeFound: if (lstrcmpi(rsArg.ms_Arg, L"/C") == 0 || lstrcmpi(rsArg.ms_Arg, L"/K") == 0) rsArg.mn_CmdCall = CmdArg::cc_CmdCK; else if ((rsArg.ms_Arg[0] != L'/') && (rsArg.ms_Arg[0] != L'-')) rsArg.mn_CmdCall = CmdArg::cc_Undefined; break; } *asCmdLine = psCmdLine; return 0; }
// Returns true on changes // bDeQuote: replace two "" with one " // bDeEscape: process special symbols: ^e^[^r^n^t^b bool DemangleArg(CmdArg& 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; }