LPCWSTR GetDrive(LPCWSTR pszPath, wchar_t* szDrive, int/*countof(szDrive)*/ cchDriveMax) { if (!szDrive || cchDriveMax < 16) return NULL; if (pszPath[0] != L'\\' && pszPath[1] == L':') { lstrcpyn(szDrive, pszPath, 3); } else if (lstrcmpni(pszPath, L"\\\\?\\UNC\\", 8) == 0) { // UNC format? "\\?\UNC\Server\Share" LPCWSTR pszSlash = wcschr(pszPath+8, L'\\'); // point to end of server name pszSlash = pszSlash ? wcschr(pszSlash+1, L'\\') : NULL; // point to end of share name lstrcpyn(szDrive, pszPath, pszSlash ? (int)min((INT_PTR)cchDriveMax,pszSlash-pszPath+1) : cchDriveMax); } else if (lstrcmpni(pszPath, L"\\\\?\\", 4) == 0 && pszPath[4] && pszPath[5] == L':') { lstrcpyn(szDrive, pszPath, 7); } else if (pszPath[0] == L'\\' && pszPath[1] == L'\\') { // "\\Server\Share" LPCWSTR pszSlash = wcschr(pszPath+2, L'\\'); // point to end of server name pszSlash = pszSlash ? wcschr(pszSlash+1, L'\\') : NULL; // point to end of share name lstrcpyn(szDrive, pszPath, pszSlash ? (int)min((INT_PTR)cchDriveMax,pszSlash-pszPath+1) : cchDriveMax); } else { lstrcpyn(szDrive, L"", cchDriveMax); } return szDrive; }
bool CmdArg::CompareSwitch(LPCWSTR asSwitch) const { if ((asSwitch[0] == L'-') || (asSwitch[0] == L'/')) { asSwitch++; } else { _ASSERTE((asSwitch[0] == L'-') || (asSwitch[0] == L'/')); } int iCmp = lstrcmpi(ms_Val+1, asSwitch); if (iCmp == 0) return true; // Support partial comparison for L"-inside=..." when (asSwitch == L"-inside=") int len = lstrlen(asSwitch); if ((len > 1) && ((asSwitch[len-1] == L'=') || (asSwitch[len-1] == L':'))) { iCmp = lstrcmpni(ms_Val+1, asSwitch, (len - 1)); if ((iCmp == 0) && ((ms_Val[len] == L'=') || (ms_Val[len] == L':'))) return true; } return false; }
UINT GetCpFromString(LPCWSTR asString, LPCWSTR* ppszEnd /*= NULL*/) { UINT nCP = 0; wchar_t* pszEnd = NULL; struct KnownCpList { LPCWSTR pszName; DWORD_PTR nCP; } CP[] = { // longer - first {L"utf-8", CP_UTF8}, {L"utf8", CP_UTF8}, {L"ansicp", CP_ACP}, {L"ansi", CP_ACP}, {L"acp", CP_ACP}, {L"oemcp", CP_OEMCP}, {L"oem", CP_OEMCP}, {NULL} }; if (!asString) goto wrap; for (KnownCpList* p = CP; p->pszName; p++) { int iLen = lstrlen(p->pszName); if (lstrcmpni(asString, p->pszName, iLen) == 0) { // После имени могут быть разделители (знаки пунктуации) if (asString[iLen] == 0 || (asString[iLen] && wcschr(L",.:; \t\r\n", asString[iLen]))) { nCP = LODWORD(p->nCP); pszEnd = (wchar_t*)asString+iLen; // CP_ACP is 0, so jump to wrap instead of break goto wrap; } } } nCP = wcstoul(asString, &pszEnd, 10); wrap: if (ppszEnd) *ppszEnd = pszEnd; if (nCP <= 0xFFFF) return nCP; return 0; }
// Comes from ConEmu's settings (Environment setting page) void CProcessEnvCmd::AddLines(LPCWSTR asLines, bool bPriority) { LPCWSTR pszLines = asLines; CEStr lsLine; INT_PTR nBefore = bPriority ? 0 : -1; while (0 == NextLine(&pszLines, lsLine)) { // Skip empty lines LPCWSTR pszLine = SkipNonPrintable(lsLine); if (!pszLine || !*pszLine) continue; // A comment? if ((pszLine[0] == L'#') || ((pszLine[0] == L'/') && (pszLine[1] == L'/')) || ((pszLine[0] == L'-') && (pszLine[1] == L'-')) || (lstrcmpni(pszLine, L"REM ", 4) == 0) ) continue; // Process this line if (AddCommands(pszLine, NULL, true, nBefore) && bPriority) nBefore++; } }
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; int iRc = 0; BOOL lbFirstWasGot = FALSE; LPCWSTR pwszCopy; int nLastChar; #ifdef _DEBUG CEStr szDbgFirst; #endif if (!asCmdLine || !*asCmdLine) { _ASSERTE(asCmdLine && *asCmdLine); goto wrap; } #ifdef _DEBUG // Это минимальные проверки, собственно к коду - не относятся 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(); // `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); // If this is a full path without environment variables if (((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[0] == 0) { if ((iRc = NextArg(&pwszCopy, szExe)) != 0) { //Parsing command line failed #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } _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; }
// Return true if "SetEnvironmentVariable" was processed // if (bDoSet==false) - just skip all "set" commands // Supported commands: // set abc=val // "set PATH=C:\Program Files;%PATH%" // chcp [utf8|ansi|oem|<cp_no>] // title "Console init title" bool ProcessSetEnvCmd(LPCWSTR& asCmdLine, bool bDoSet, CmdArg* rpsTitle /*= NULL*/) { LPCWSTR lsCmdLine = asCmdLine; bool bEnvChanged = false; CmdArg lsSet, lsAmp; // Example: "set PATH=C:\Program Files;%PATH%" & set abc=def & cmd while (NextArg(&lsCmdLine, lsSet) == 0) { bool bTokenOk = false; wchar_t* lsNameVal = NULL; // It may contains only "set" if was not quoted if (lstrcmpi(lsSet, L"set") == 0) { // Now we shell get in lsSet "abc=def" token if ((NextArg(&lsCmdLine, lsSet) == 0) && (wcschr(lsSet, L'=') > lsSet.ms_Arg)) { lsNameVal = lsSet.ms_Arg; } } // Or full "set PATH=C:\Program Files;%PATH%" command (without quotes ATM) else if (lstrcmpni(lsSet, L"set ", 4) == 0) { LPCWSTR psz = SkipNonPrintable(lsSet.ms_Arg+4); if (wcschr(psz, L'=') > psz) { lsNameVal = (wchar_t*)psz; } } // Process "chcp <cp>" too else if (lstrcmpi(lsSet, L"chcp") == 0) { if (NextArg(&lsCmdLine, lsSet) == 0) { UINT nCP = GetCpFromString(lsSet); if (nCP > 0 && nCP <= 0xFFFF) { bTokenOk = true; _ASSERTE(lsNameVal == NULL); // Ask to be changed? if (bDoSet) { //Issue 60: BUGBUG: На некоторых системых (Win2k3, WinXP) SetConsoleCP (и иже с ними) просто зависают DWORD nTID; HANDLE hThread = CreateThread(NULL, 0, OurSetConsoleCPThread, (LPVOID)nCP, 0, &nTID); if (hThread) { DWORD nWait = WaitForSingleObject(hThread, 1000); if (nWait == WAIT_TIMEOUT) { TerminateThread(hThread,100); } CloseHandle(hThread); } } } } } // Change title without need of cmd.exe else if (lstrcmpi(lsSet, L"title") == 0) { if (NextArg(&lsCmdLine, lsSet) == 0) { bTokenOk = true; _ASSERTE(lsNameVal == NULL); // Ask to be changed? if (rpsTitle) rpsTitle->Set(lsSet); } } // Well, known command was detected. What is next? if (lsNameVal || bTokenOk) { lsAmp.GetPosFrom(lsSet); if (NextArg(&lsCmdLine, lsAmp) != 0) { // End of command? Use may call only "set" without following app? Run simple "cmd" in that case _ASSERTE(lsCmdLine!=NULL && *lsCmdLine==0); bTokenOk = true; // And process SetEnvironmentVariable } else { if (lstrcmp(lsAmp, L"&") == 0) { // Only simple conveyer is supported! bTokenOk = true; // And process SetEnvironmentVariable } // Update last pointer (debug and asserts purposes) lsSet.GetPosFrom(lsAmp); } } if (!bTokenOk) { break; // Stop processing command line } else if (lsNameVal) { // And split name/value _ASSERTE(lsNameVal!=NULL); wchar_t* pszEq = wcschr(lsNameVal, L'='); if (!pszEq) { _ASSERTE(pszEq!=NULL); break; } if (bDoSet) { *(pszEq++) = 0; // Expand value wchar_t* pszExpanded = ExpandEnvStr(pszEq); LPCWSTR pszSet = pszExpanded ? pszExpanded : pszEq; SetEnvironmentVariable(lsNameVal, (pszSet && *pszSet) ? pszSet : NULL); SafeFree(pszExpanded); } bEnvChanged = true; } // Remember processed position asCmdLine = lsCmdLine; } // end of "while (NextArg(&lsCmdLine, lsSet) == 0)" // Fin if (!asCmdLine || !*asCmdLine) { static wchar_t szSimple[] = L"cmd"; asCmdLine = szSimple; } return bEnvChanged; }
bool CConEmuUpdate::StartLocalUpdate(LPCWSTR asDownloadedPackage) { bool bRc = false; LPCWSTR pszName, pszExt; HANDLE hTarget = NULL; wchar_t *pszLocalPackage = NULL, *pszBatchFile = NULL; DWORD nLocalCRC = 0; BOOL lbDownloadRc = FALSE, lbExecuteRc = FALSE; LPCWSTR pszPackPref = L"conemupack."; size_t lnPackPref = _tcslen(pszPackPref); LPCWSTR pszSetupPref = L"conemusetup."; size_t lnSetupPref = _tcslen(pszSetupPref); _ASSERTE(gpConEmu && gpConEmu->isMainThread()); if (InUpdate() != us_NotStarted) { MBoxError(L"Checking for updates already started"); goto wrap; } if (mb_InCheckProcedure) { Assert(mb_InCheckProcedure==FALSE); goto wrap; } DeleteBadTempFiles(); Inet.Deinit(true); pszName = PointToName(asDownloadedPackage); pszExt = PointToExt(pszName); if (!pszName || !*pszName || !pszExt || !*pszExt) { AssertMsg(L"Invalid asDownloadedPackage"); goto wrap; } // Запомнить текущие параметры обновления if (!mp_Set) mp_Set = new ConEmuUpdateSettings; mp_Set->LoadFrom(&gpSet->UpdSet); mb_ManualCallMode = TRUE; // Clear possible last error { MSectionLock SC; SC.Lock(mp_LastErrorSC, TRUE); SafeFree(ms_LastErrorInfo); } ms_NewVersion[0] = 0; if ((lstrcmpni(pszName, pszPackPref, lnPackPref) == 0) && (lstrcmpi(pszExt, L".7z") == 0) && (((pszExt - pszName) - lnPackPref + 1) < sizeof(ms_NewVersion))) { // Check it was NOT installed with "Setupper" if (mp_Set->UpdateDownloadSetup() == 1) { DontEnable de; LPCWSTR pszConfirm = L"ConEmu was installed with setup!\nAre you sure to update installation with 7zip?"; int iBtn = MessageBox(NULL, pszConfirm, ms_DefaultTitle, MB_ICONEXCLAMATION|MB_SETFOREGROUND|MB_SYSTEMMODAL|MB_YESNO|MB_DEFBUTTON2); if (iBtn != IDYES) { goto wrap; } } if (!Check7zipInstalled()) goto wrap; // Error already reported // Forcing usage of 7zip package! mp_Set->isUpdateDownloadSetup = 2; //if (!CanUpdateInstallation()) //{ // // Значит 7zip обломается при попытке распаковки // goto wrap; //} // OK size_t nLen = (pszExt - pszName) - lnPackPref; wmemmove(ms_NewVersion, pszName+lnPackPref, nLen); ms_NewVersion[nLen] = 0; } else if ((lstrcmpni(pszName, pszSetupPref, lnSetupPref) == 0) && (lstrcmpi(pszExt, L".exe") == 0) && (((pszExt - pszName) - lnSetupPref + 1) < sizeof(ms_NewVersion))) { // Must be installed with "Setupper" if (mp_Set->UpdateDownloadSetup() != 1) { MBoxError(L"ConEmu was not installed with setup! Can't update!"); goto wrap; } // OK size_t nLen = (pszExt - pszName) - lnSetupPref; wmemmove(ms_NewVersion, pszName+lnSetupPref, nLen); ms_NewVersion[nLen] = 0; } else { AssertMsg(L"Invalid asDownloadedPackage (2)"); goto wrap; } // Сразу проверим, как нужно будет запускаться bNeedRunElevation = NeedRunElevation(); _wsprintf(ms_CurVersion, SKIPLEN(countof(ms_CurVersion)) L"%02u%02u%02u%s", (MVV_1%100),MVV_2,MVV_3,_T(MVV_4a)); //ms_NewVersion // StartLocalUpdate - запуск обновления из локального пакета mb_InetMode = false; mb_DroppedMode = true; pszLocalPackage = CreateTempFile(mp_Set->szUpdateDownloadPath, PointToName(asDownloadedPackage), hTarget); if (!pszLocalPackage) goto wrap; lbDownloadRc = DownloadFile(asDownloadedPackage, pszLocalPackage, hTarget, nLocalCRC, TRUE); CloseHandle(hTarget); if (!lbDownloadRc) goto wrap; if (mb_RequestTerminate) goto wrap; pszBatchFile = CreateBatchFile(pszLocalPackage); if (!pszBatchFile) goto wrap; if (!QueryConfirmation(us_ConfirmUpdate)) { goto wrap; } Assert(mb_ManualCallMode==TRUE); Assert(mpsz_PendingBatchFile==NULL); mpsz_PendingPackageFile = pszLocalPackage; pszLocalPackage = NULL; mpsz_PendingBatchFile = pszBatchFile; pszBatchFile = NULL; m_UpdateStep = us_ExitAndUpdate; if (gpConEmu) gpConEmu->RequestExitUpdate(); lbExecuteRc = TRUE; wrap: _ASSERTE(mpsz_DeleteIniFile==NULL); _ASSERTE(mpsz_DeletePackageFile==NULL); mpsz_DeletePackageFile = NULL; if (pszLocalPackage) { if (*pszLocalPackage && (!lbDownloadRc || (!lbExecuteRc && !mp_Set->isUpdateLeavePackages))) mpsz_DeletePackageFile = pszLocalPackage; //DeleteFile(pszLocalPackage); else SafeFree(pszLocalPackage); } _ASSERTE(mpsz_DeleteBatchFile==NULL); mpsz_DeleteBatchFile = NULL; if (pszBatchFile) { if (*pszBatchFile && !lbExecuteRc) mpsz_DeleteBatchFile = pszBatchFile; //DeleteFile(pszBatchFile); else SafeFree(pszBatchFile); } if (!lbExecuteRc) { m_UpdateStep = us_NotStarted; mb_DroppedMode = false; } return bRc; }
//------------------------------------------------------------------------ ///| Parsing the command line |/////////////////////////////////////////// //------------------------------------------------------------------------ // Returns: // true - continue normal startup // false - exit process with iResult code bool CConEmuStart::ParseCommandLine(LPCWSTR pszCmdLine, int& iResult) { bool bRc = false; iResult = 100; _ASSERTE(pszCmdLine!=NULL); opt.cmdLine.Set(pszCmdLine ? pszCmdLine : L""); // pszCmdLine *may* or *may not* start with our executable or full path to our executable LPCWSTR pszTemp = opt.cmdLine; LPCWSTR cmdLineRest = SkipNonPrintable(opt.cmdLine); LPCWSTR pszName, pszArgStart; LPCWSTR psUnknown = NULL; CEStr szArg, szNext; CEStr szExeName, szExeNameOnly; // Set %ConEmuArgs% env var // It may be usefull if we need to restart ConEmu // from batch/script with the same arguments (selfupdate etc.) LPCWSTR pszCopyToEnvStart = NULL; // Have to get our exectuable name and name without extension szExeName.Set(PointToName(gpConEmu->ms_ConEmuExe)); szExeNameOnly.Set(szExeName); wchar_t* pszDot = (wchar_t*)PointToExt(szExeNameOnly.ms_Val); _ASSERTE(pszDot); if (pszDot) *pszDot = 0; // Check the first argument in the command line (most probably it will be our executable path/name) if (NextArg(&pszTemp, szArg) != 0) { _ASSERTE(FALSE && "GetCommandLine() is empty"); // Treat as empty command line, allow to start bRc = true; iResult = 0; goto wrap; } pszName = PointToName(szArg); if ((lstrcmpi(pszName, szExeName) == 0) || (lstrcmpi(pszName, szExeNameOnly) == 0)) { // OK, our executable was specified properly in the command line _ASSERTE(*pszTemp != L' '); cmdLineRest = SkipNonPrintable(pszTemp); } // Must be empty at the moment _ASSERTE(opt.runCommand.IsEmpty()); // Does the command line contain our switches? // Or we need to append all switches to starting shell? if (cmdLineRest && *cmdLineRest) { pszTemp = cmdLineRest; if (NextArg(&pszTemp, szArg) == 0) { if ((*szArg.ms_Val != L'/') && (*szArg.ms_Val != L'-') /*&& !wcschr(szArg.ms_Val, L'/')*/ ) { // Save it for further use opt.runCommand.Set(cmdLineRest); // And do not process it (no switches at all) cmdLineRest = NULL; opt.params = -1; } } } // Let parse the reset szArg.Empty(); szNext.Empty(); // Processing loop begin if (cmdLineRest && *cmdLineRest) { pszCopyToEnvStart = cmdLineRest; opt.cfgSwitches.Set(pszCopyToEnvStart); while (NextArg(&cmdLineRest, szArg, &pszArgStart) == 0) { bool lbNotFound = false; // ':' removed from checks because otherwise it will not warn // on invalid usage of "-new_console:a" for example if (szArg.ms_Val[0] == L'-' && szArg.ms_Val[1] && !wcspbrk(szArg.ms_Val+1, L"\\//|.&<>^")) { // Seems this is to be the "switch" too // Use both notations ('-' and '/') *szArg.ms_Val = L'/'; } LPCWSTR curCommand = szArg.ms_Val; #define NeedNextArg() \ if (NextArg(&cmdLineRest, szNext) != 0) { iResult = 101; goto wrap; } \ curCommand = szNext.ms_Val; if (*curCommand != L'/') { continue; // Try next switch? } else { opt.params++; if (!klstricmp(curCommand, _T("/autosetup"))) { BOOL lbTurnOn = TRUE; NeedNextArg(); if (*curCommand == _T('0')) { lbTurnOn = FALSE; } else { NeedNextArg(); DWORD dwAttr = GetFileAttributes(curCommand); if (dwAttr == (DWORD)-1 || (dwAttr & FILE_ATTRIBUTE_DIRECTORY)) { iResult = 102; goto wrap; } } HKEY hk = NULL; DWORD dw; int nSetupRc = 100; if (0 != RegCreateKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Command Processor"), 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hk, &dw)) { iResult = 103; goto wrap; } if (lbTurnOn) { size_t cchMax = _tcslen(curCommand); LPCWSTR pszArg1 = NULL; if (*cmdLineRest) { // May be ‘/GHWND=NEW’ or smth else pszArg1 = cmdLineRest; cchMax += _tcslen(pszArg1); } cchMax += 16; // + quotations, spaces and so on wchar_t* pszCmd = (wchar_t*)calloc(cchMax, sizeof(*pszCmd)); _wsprintf(pszCmd, SKIPLEN(cchMax) L"\"%s\"%s%s%s", curCommand, pszArg1 ? L" \"" : L"", pszArg1 ? pszArg1 : L"", pszArg1 ? L"\"" : L""); if (0 == RegSetValueEx(hk, _T("AutoRun"), 0, REG_SZ, (LPBYTE)pszCmd, (DWORD)sizeof(*pszCmd)*(_tcslen(pszCmd)+1))) nSetupRc = 1; free(pszCmd); } else { if (0==RegDeleteValue(hk, _T("AutoRun"))) nSetupRc = 1; } RegCloseKey(hk); // сбросить CreateInNewEnvironment для ConMan ResetConman(); iResult = nSetupRc; goto wrap; } else if (!klstricmp(curCommand, _T("/bypass")) || !klstricmp(curCommand, _T("/apparent")) || !klstricmp(curCommand, _T("/system")) || !klstricmp(curCommand, _T("/interactive")) || !klstricmp(curCommand, _T("/demote"))) { // -bypass // Этот ключик был придуман для прозрачного запуска консоли // в режиме администратора // (т.е. чтобы окно UAC нормально всплывало, но не мелькало консольное окно) // Но не получилось, пока требуются хэндлы процесса, а их не получается // передать в НЕ приподнятый процесс (исходный ConEmu GUI). // -apparent // Same as -bypass, but run the process as SW_SHOWNORMAL // -demote // Запуск процесса (ком.строка после "/demote") в режиме простого юзера, // когда текущий процесс уже запущен "под админом". "Понизить" текущие // привилегии просто так нельзя, поэтому запуск идет через TaskSheduler. // -system // Non-interactive process, started as System account // It's used when starting consoles, our server works fine as non-interactive // -interactive // Used when ConEmu.exe is started under System account, // but we need to give starting process interactive capabilities. _ASSERTE(opt.runCommand.IsEmpty()); pszTemp = cmdLineRest; if ((NextArg(&pszTemp, szNext) == 0) && (szNext.ms_Val[0] == L'-' || szNext.ms_Val[0] == L'/') && (lstrcmpi(szNext.ms_Val+1, L"cmd") == 0)) { opt.runCommand.Set(pszTemp); } else { opt.runCommand.Set(cmdLineRest); } if (opt.runCommand.IsEmpty()) { CEStr lsMsg(L"Invalid cmd line. '", curCommand, L"' exists, command line is empty"); DisplayLastError(lsMsg, -1); goto wrap; } // Information #ifdef _DEBUG STARTUPINFO siOur = {sizeof(siOur)}; GetStartupInfo(&siOur); #endif STARTUPINFO si = {sizeof(si)}; PROCESS_INFORMATION pi = {}; si.dwFlags = STARTF_USESHOWWINDOW; // Only `-demote` and `-apparent` switches were implemented to start application visible // All others are intended to run our server process, without blinking of course if ((0 == klstricmp(curCommand, _T("/demote"))) || (0 == klstricmp(curCommand, _T("/apparent")))) si.wShowWindow = SW_SHOWNORMAL; else si.wShowWindow = SW_HIDE; wchar_t szCurDir[MAX_PATH+1] = L""; GetCurrentDirectory(countof(szCurDir), szCurDir); BOOL b; DWORD nErr = 0; // if we were started from TaskScheduler, it would be nice to wait a little // to let parent (creator of the scheduler task) know we were started successfully bool bFromScheduler = false; // Log the command to be started { CEStr lsLog( L"Starting process", L": ", curCommand, L" `", opt.runCommand.ms_Val, L"`"); LogString(lsLog); } if (!klstricmp(curCommand, _T("/demote"))) { b = CreateProcessDemoted(opt.runCommand.ms_Val, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, szCurDir, &si, &pi, &nErr); } else if (!klstricmp(curCommand, _T("/system"))) { b = CreateProcessSystem(opt.runCommand.ms_Val, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, szCurDir, &si, &pi); } else if (!klstricmp(curCommand, _T("/interactive"))) { b = CreateProcessInteractive((DWORD)-1, NULL, opt.runCommand.ms_Val, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, szCurDir, &si, &pi, &nErr); bFromScheduler = true; } else // -bypass, -apparent { b = CreateProcess(NULL, opt.runCommand.ms_Val, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); nErr = b ? 0 : GetLastError(); bFromScheduler = true; } // Log the result { CEStr lsLog; wchar_t szExtra[32] = L""; if (b) { if (pi.dwProcessId) _wsprintf(szExtra, SKIPCOUNT(szExtra) L", PID=%u", pi.dwProcessId); lsLog = lstrmerge( L"Process was created successfully", szExtra); } else { _wsprintf(szExtra, SKIPCOUNT(szExtra) L", ErrorCode=%u", nErr); lsLog = lstrmerge( L"Failed to start process", szExtra); } LogString(lsLog); } // If the error was not shown yet if (nErr) DisplayLastError(opt.runCommand, nErr); // if we were started from TaskScheduler, it would be nice to wait a little // to let parent (creator of the scheduler task) know we were started successfully if (bFromScheduler) { LogString(L"Sleeping for 5 seconds"); Sleep(5*1000); } // Success? if (b) { iResult = 0; } // Done, close handles, if they were opened SafeCloseHandle(pi.hProcess); SafeCloseHandle(pi.hThread); goto wrap; } else if (!klstricmp(curCommand, _T("/multi"))) { gpConEmu->AppendExtraArgs(curCommand); gpConEmu->opt.MultiConValue = true; } else if (!klstricmp(curCommand, _T("/nomulti"))) { gpConEmu->AppendExtraArgs(curCommand); gpConEmu->opt.MultiConValue = false; } else if (!klstricmp(curCommand, _T("/visible"))) { gpConEmu->opt.VisValue = true; } else if (!klstricmp(curCommand, _T("/ct")) || !klstricmp(curCommand, _T("/cleartype")) || !klstricmp(curCommand, _T("/ct0")) || !klstricmp(curCommand, _T("/ct1")) || !klstricmp(curCommand, _T("/ct2"))) { switch (curCommand[3]) { case L'0': gpConEmu->opt.ClearTypeVal = NONANTIALIASED_QUALITY; break; case L'1': gpConEmu->opt.ClearTypeVal = ANTIALIASED_QUALITY; break; default: gpConEmu->opt.ClearTypeVal = CLEARTYPE_NATURAL_QUALITY; } } // Interface language else if (!klstricmp(curCommand, _T("/lng"))) { NeedNextArg(); if (!gpConEmu->opt.Language.Exists) { gpConEmu->opt.Language = curCommand; gpConEmu->AppendExtraArgs(L"/lng", curCommand); } } // Optional specific "ConEmu.l10n" else if (!klstricmp(curCommand, _T("/lngfile"))) { NeedNextArg(); if (!gpConEmu->opt.LanguageFile.Exists) { gpConEmu->opt.LanguageFile = curCommand; gpConEmu->AppendExtraArgs(L"/lngfile", curCommand); } } // имя шрифта else if (!klstricmp(curCommand, _T("/font"))) { NeedNextArg(); if (!gpConEmu->opt.FontVal.Exists) { gpConEmu->opt.FontVal = curCommand; gpConEmu->AppendExtraArgs(L"/font", curCommand); } } // Высота шрифта else if (!klstricmp(curCommand, _T("/size"))) { NeedNextArg(); if (!gpConEmu->opt.SizeVal.Exists) { gpConEmu->opt.SizeVal.SetInt(curCommand); } } // ADD fontname; by Mors else if (!klstricmp(curCommand, _T("/fontfile"))) { CESwitch szFile(sw_Str); if (!GetCfgParm(cmdLineRest, szFile, MAX_PATH)) { goto wrap; } gpConEmu->AppendExtraArgs(L"/fontfile", szFile.GetStr()); gpFontMgr->RegisterFont(szFile.GetStr(), TRUE); } // Register all fonts from specified directory else if (!klstricmp(curCommand, _T("/fontdir"))) { CESwitch szDir(sw_Str); if (!GetCfgParm(cmdLineRest, szDir, MAX_PATH)) { goto wrap; } gpConEmu->AppendExtraArgs(L"/fontdir", szDir.GetStr()); gpFontMgr->RegisterFontsDir(szDir.GetStr()); } else if (!klstricmp(curCommand, _T("/fs"))) { gpConEmu->opt.WindowModeVal = wmFullScreen; } else if (!klstricmp(curCommand, _T("/max"))) { gpConEmu->opt.WindowModeVal = wmMaximized; } else if (!klstricmp(curCommand, _T("/min")) || !klstricmp(curCommand, _T("/mintsa")) || !klstricmp(curCommand, _T("/starttsa"))) { gpConEmu->WindowStartMinimized = true; if (klstricmp(curCommand, _T("/min")) != 0) { gpConEmu->WindowStartTsa = true; gpConEmu->WindowStartNoClose = (klstricmp(curCommand, _T("/mintsa")) == 0); } } else if (!klstricmp(curCommand, _T("/tsa")) || !klstricmp(curCommand, _T("/tray"))) { gpConEmu->ForceMinimizeToTray = true; } else if (!klstricmp(curCommand, _T("/detached"))) { gpConEmu->m_StartDetached = crb_On; } else if (!klstricmp(curCommand, _T("/here"))) { gpConEmu->mb_ConEmuHere = true; gpConEmu->StoreWorkDir(); } else if (!klstricmp(curCommand, _T("/update"))) { gpConEmu->opt.AutoUpdateOnStart = true; } else if (!klstricmp(curCommand, _T("/noupdate"))) { // This one has more weight than AutoUpdateOnStart gpConEmu->opt.DisableAutoUpdate = true; } else if (!klstricmp(curCommand, _T("/nokeyhook")) || !klstricmp(curCommand, _T("/nokeyhooks")) || !klstricmp(curCommand, _T("/nokeybhook")) || !klstricmp(curCommand, _T("/nokeybhooks"))) { gpConEmu->DisableKeybHooks = true; } else if (!klstricmp(curCommand, _T("/nocloseconfirm"))) { gpConEmu->DisableCloseConfirm = true; } else if (!klstricmp(curCommand, _T("/nomacro"))) { gpConEmu->DisableAllMacro = true; } else if (!klstricmp(curCommand, _T("/nohotkey")) || !klstricmp(curCommand, _T("/nohotkeys"))) { gpConEmu->DisableAllHotkeys = true; } else if (!klstricmp(curCommand, _T("/nodeftrm")) || !klstricmp(curCommand, _T("/nodefterm"))) { gpConEmu->DisableSetDefTerm = true; } else if (!klstricmp(curCommand, _T("/noregfont")) || !klstricmp(curCommand, _T("/noregfonts"))) { gpConEmu->DisableRegisterFonts = true; } else if (!klstricmp(curCommand, _T("/inside")) || !lstrcmpni(curCommand, _T("/inside="), 8)) { bool bRunAsAdmin = isPressed(VK_SHIFT); bool bSyncDir = false; LPCWSTR pszSyncFmt = NULL; gpConEmu->mb_ConEmuHere = true; gpConEmu->StoreWorkDir(); if (curCommand[7] == _T('=')) { bSyncDir = true; pszSyncFmt = curCommand+8; // \eCD /d %1 - \e - ESC, \b - BS, \n - ENTER, %1 - "dir", %2 - "bash dir" } CConEmuInside::InitInside(bRunAsAdmin, bSyncDir, pszSyncFmt, 0, NULL); } else if (!klstricmp(curCommand, _T("/insidepid"))) { NeedNextArg(); bool bRunAsAdmin = isPressed(VK_SHIFT); wchar_t* pszEnd; // Здесь указывается PID, в который нужно внедриться. DWORD nInsideParentPID = wcstol(curCommand, &pszEnd, 10); if (nInsideParentPID) { CConEmuInside::InitInside(bRunAsAdmin, false, NULL, nInsideParentPID, NULL); } } else if (!klstricmp(curCommand, _T("/insidewnd"))) { NeedNextArg(); if (curCommand[0] == L'0' && (curCommand[1] == L'x' || curCommand[1] == L'X')) curCommand += 2; else if (curCommand[0] == L'x' || curCommand[0] == L'X') curCommand ++; bool bRunAsAdmin = isPressed(VK_SHIFT); wchar_t* pszEnd; // Здесь указывается HWND, в котором нужно создаваться. HWND hParent = (HWND)(DWORD_PTR)wcstoul(curCommand, &pszEnd, 16); if (hParent && IsWindow(hParent)) { CConEmuInside::InitInside(bRunAsAdmin, false, NULL, 0, hParent); } } else if (!klstricmp(curCommand, _T("/icon"))) { NeedNextArg(); if (!gpConEmu->opt.IconPrm.Exists && *curCommand) { gpConEmu->opt.IconPrm = true; gpConEmu->mps_IconPath = ExpandEnvStr(curCommand); } } else if (!klstricmp(curCommand, _T("/dir"))) { NeedNextArg(); if (*curCommand) { // Например, "%USERPROFILE%" wchar_t* pszExpand = NULL; if (wcschr(curCommand, L'%') && ((pszExpand = ExpandEnvStr(curCommand)) != NULL)) { gpConEmu->StoreWorkDir(pszExpand); SafeFree(pszExpand); } else { gpConEmu->StoreWorkDir(curCommand); } } } else if (!klstricmp(curCommand, _T("/updatejumplist"))) { // Copy current Task list to Win7 Jump list (Taskbar icon) gpConEmu->mb_UpdateJumpListOnStartup = true; } else if (!klstricmp(curCommand, L"/log") || !klstricmp(curCommand, L"/log0") || !klstricmp(curCommand, L"/log1") || !klstricmp(curCommand, L"/log2") || !klstricmp(curCommand, L"/log3") || !klstricmp(curCommand, L"/log4")) { if (!klstricmp(curCommand, L"/log") || !klstricmp(curCommand, L"/log0")) gpConEmu->opt.AdvLogging.SetInt(1); else gpConEmu->opt.AdvLogging.SetInt((BYTE)(curCommand[4] - L'0')); // 1..4 // Do create logging service DEBUGSTRSTARTUP(L"Creating log file"); gpConEmu->CreateLog(); } else if (!klstricmp(curCommand, _T("/single")) || !klstricmp(curCommand, _T("/reuse"))) { // "/reuse" switch to be remastered gpConEmu->AppendExtraArgs(curCommand); gpSetCls->SingleInstanceArg = sgl_Enabled; } else if (!klstricmp(curCommand, _T("/nosingle"))) { gpConEmu->AppendExtraArgs(curCommand); gpSetCls->SingleInstanceArg = sgl_Disabled; } else if (!klstricmp(curCommand, _T("/DesktopMode"))) { gpConEmu->opt.DesktopMode = true; } else if (!klstricmp(curCommand, _T("/quake")) || !klstricmp(curCommand, _T("/quakeauto")) || !klstricmp(curCommand, _T("/noquake"))) { if (!klstricmp(curCommand, _T("/quake"))) gpConEmu->opt.QuakeMode = 1; else if (!klstricmp(curCommand, _T("/quakeauto"))) gpConEmu->opt.QuakeMode = 2; else { gpConEmu->opt.QuakeMode = 0; if (gpSetCls->SingleInstanceArg == sgl_Default) gpSetCls->SingleInstanceArg = sgl_Disabled; } } else if (!klstricmp(curCommand, _T("/showhide")) || !klstricmp(curCommand, _T("/showhideTSA"))) { gpSetCls->SingleInstanceArg = sgl_Enabled; gpSetCls->SingleInstanceShowHide = !klstricmp(curCommand, _T("/showhide")) ? sih_ShowMinimize : sih_ShowHideTSA; } else if (!klstricmp(curCommand, _T("/reset")) || !klstricmp(curCommand, _T("/resetdefault")) || !klstricmp(curCommand, _T("/basic"))) { gpConEmu->opt.ResetSettings = true; if (!klstricmp(curCommand, _T("/resetdefault"))) { gpSetCls->isFastSetupDisabled = true; } else if (!klstricmp(curCommand, _T("/basic"))) { gpSetCls->isFastSetupDisabled = true; gpSetCls->isResetBasicSettings = true; } } else if (!klstricmp(curCommand, _T("/nocascade")) || !klstricmp(curCommand, _T("/dontcascade"))) { gpConEmu->AppendExtraArgs(curCommand); gpSetCls->isDontCascade = true; } else if (!klstricmp(curCommand, _T("/WndX")) || !klstricmp(curCommand, _T("/WndY")) || !klstricmp(curCommand, _T("/WndW")) || !klstricmp(curCommand, _T("/WndWidth")) || !klstricmp(curCommand, _T("/WndH")) || !klstricmp(curCommand, _T("/WndHeight"))) { TCHAR ch = curCommand[4]; CharUpperBuff(&ch, 1); CESwitch psz(sw_Str); bool bParm = false; if (!GetCfgParm(cmdLineRest, bParm, psz, 32)) { goto wrap; } gpConEmu->opt.SizePosPrm = true; // Direct X/Y implies /nocascade if (ch == _T('X') || ch == _T('Y')) { // TODO: isDontCascade must be in our opt struct !!! gpSetCls->isDontCascade = true; } switch (ch) { case _T('X'): gpConEmu->opt.sWndX.SetStr(psz.Str, sw_Str); break; case _T('Y'): gpConEmu->opt.sWndY.SetStr(psz.Str, sw_Str); break; case _T('W'): gpConEmu->opt.sWndW.SetStr(psz.Str, sw_Str); break; case _T('H'): gpConEmu->opt.sWndH.SetStr(psz.Str, sw_Str); break; } } else if (!klstricmp(curCommand, _T("/Monitor"))) { CESwitch psz(sw_Str); bool bParm = false; if (!GetCfgParm(cmdLineRest, bParm, psz, 64)) { goto wrap; } if ((gpConEmu->opt.Monitor.Mon = MonitorFromParam(psz.Str)) != NULL) { gpConEmu->opt.Monitor.Exists = true; gpConEmu->opt.Monitor.Type = sw_Int; gpStartEnv->hStartMon = gpConEmu->opt.Monitor.Mon; } } else if (!klstricmp(curCommand, _T("/Buffer")) || !klstricmp(curCommand, _T("/BufferHeight"))) { NeedNextArg(); if (!gpConEmu->opt.BufferHeightVal.Exists) { gpConEmu->opt.BufferHeightVal.SetInt(curCommand); if (gpConEmu->opt.BufferHeightVal.GetInt() < 0) { //setParent = true; -- Maximus5 - нефиг, все ручками gpConEmu->opt.BufferHeightVal = -gpConEmu->opt.BufferHeightVal.GetInt(); } if (gpConEmu->opt.BufferHeightVal.GetInt() < LONGOUTPUTHEIGHT_MIN) gpConEmu->opt.BufferHeightVal = LONGOUTPUTHEIGHT_MIN; else if (gpConEmu->opt.BufferHeightVal.GetInt() > LONGOUTPUTHEIGHT_MAX) gpConEmu->opt.BufferHeightVal = LONGOUTPUTHEIGHT_MAX; } } else if (!klstricmp(curCommand, _T("/Config"))) { // -- используем последний из параметров, если их несколько if (!GetCfgParm(cmdLineRest, gpConEmu->opt.ConfigVal, 127)) { goto wrap; } } else if (!klstricmp(curCommand, _T("/Palette"))) { // -- используем последний из параметров, если их несколько if (!GetCfgParm(cmdLineRest, gpConEmu->opt.PaletteVal, MAX_PATH)) { goto wrap; } } else if (!klstricmp(curCommand, _T("/LoadRegistry"))) { gpConEmu->AppendExtraArgs(curCommand); gpConEmu->opt.ForceUseRegistryPrm = true; } else if (!klstricmp(curCommand, _T("/LoadCfgFile")) || !klstricmp(curCommand, _T("/LoadXmlFile"))) { // -- используем последний из параметров, если их несколько if (!GetCfgParm(cmdLineRest, gpConEmu->opt.LoadCfgFile, MAX_PATH, true)) { goto wrap; } } else if (!klstricmp(curCommand, _T("/SaveCfgFile")) || !klstricmp(curCommand, _T("/SaveXmlFile"))) { // -- используем последний из параметров, если их несколько if (!GetCfgParm(cmdLineRest, gpConEmu->opt.SaveCfgFile, MAX_PATH, true)) { goto wrap; } } else if (!klstricmp(curCommand, _T("/GuiMacro"))) { // -- выполняется только последний if (!GetCfgParm(cmdLineRest, gpConEmu->opt.ExecGuiMacro, 0x8000, false)) { goto wrap; } } else if (!klstricmp(curCommand, _T("/UpdateSrcSet"))) { // -- используем последний из параметров, если их несколько if (!GetCfgParm(cmdLineRest, gpConEmu->opt.UpdateSrcSet, MAX_PATH*4, false)) { goto wrap; } } else if (!klstricmp(curCommand, _T("/AnsiLog"))) { // -- используем последний из параметров, если их несколько if (!GetCfgParm(cmdLineRest, gpConEmu->opt.AnsiLogPath, MAX_PATH-40, true)) { goto wrap; } } else if (!klstricmp(curCommand, _T("/SetDefTerm"))) { gpConEmu->opt.SetUpDefaultTerminal = true; } else if (!klstricmp(curCommand, _T("/ZoneId"))) { gpConEmu->opt.FixZoneId = true; } else if (!klstricmp(curCommand, _T("/Exit"))) { gpConEmu->opt.ExitAfterActionPrm = true; } else if (!klstricmp(curCommand, _T("/QuitOnClose"))) { gpConEmu->mb_ForceQuitOnClose = true; } else if (!klstricmp(curCommand, _T("/Title"))) { bool bOk = false; CESwitch pszTitle(sw_Str); if (!GetCfgParm(cmdLineRest, bOk, pszTitle, 127)) { goto wrap; } gpConEmu->SetTitleTemplate(pszTitle.GetStr()); } else if (!klstricmp(curCommand, _T("/FindBugMode"))) { gpConEmu->mb_FindBugMode = true; } else if (!klstricmp(curCommand, _T("/debug")) || !klstricmp(curCommand, _T("/debugw")) || !klstricmp(curCommand, _T("/debugi"))) { // These switches were already processed } else if (!klstricmp(curCommand, _T("/?")) || !klstricmp(curCommand, _T("/h")) || !klstricmp(curCommand, _T("/help"))) { if (gpLng) gpLng->Reload(); ConEmuAbout::OnInfo_About(); iResult = -1; goto wrap; } // Final `-cmd ...` or `-cmdlist ...` else if ( !klstricmp(curCommand, _T("/cmd")) || !klstricmp(curCommand, _T("/cmdlist")) ) { if (opt.cfgSwitches.ms_Val) { _ASSERTE(pszArgStart>pszCopyToEnvStart); _ASSERTE((INT_PTR)(pszArgStart - pszCopyToEnvStart) <= opt.cfgSwitches.GetLen()); opt.cfgSwitches.ms_Val[pszArgStart - pszCopyToEnvStart] = 0; } opt.runCommand.Set(SkipNonPrintable(cmdLineRest)); opt.isScript = (klstricmp(curCommand, L"/cmdlist") == 0); break; } else { // Show error on unknown switch psUnknown = pszArgStart; break; } } // (*curCommand == L'/') // Avoid assertions in NextArg szArg.Empty(); szNext.Empty(); } // while (NextArg(&cmdLineRest, szArg, &pszArgStart) == 0) } // Processing loop end if (psUnknown) { DEBUGSTRSTARTUP(L"Unknown switch, exiting!"); if (gpSet->isLogging()) { // For direct logging we do not use lng resources CEStr lsLog(L"\r\n", L"Unknown switch specified: ", psUnknown, L"\r\n\r\n"); gpConEmu->LogString(lsLog, false, false); } CEStr szNewConWarn; LPCWSTR pszTestSwitch = (psUnknown[0] == L'-' || psUnknown[0] == L'/') ? ((psUnknown[1] == L'-' || psUnknown[1] == L'/') ? (psUnknown+2) : (psUnknown+1)) : psUnknown; if ((lstrcmpni(pszTestSwitch, L"new_console", 11) == 0) || (lstrcmpni(pszTestSwitch, L"cur_console", 11) == 0)) { szNewConWarn = lstrmerge(L"\r\n\r\n", CLngRc::getRsrc(lng_UnknownSwitch4/*"Switch -new_console must be specified *after* /cmd or /cmdlist"*/) ); } CEStr lsMsg( CLngRc::getRsrc(lng_UnknownSwitch1/*"Unknown switch specified:"*/), L"\r\n\r\n", psUnknown, szNewConWarn, L"\r\n\r\n", CLngRc::getRsrc(lng_UnknownSwitch2/*"Visit website to get thorough switches description:"*/), L"\r\n" CEGUIARGSPAGE L"\r\n\r\n", CLngRc::getRsrc(lng_UnknownSwitch3/*"Or run ‘ConEmu.exe -?’ to get the brief."*/) ); MBoxA(lsMsg); goto wrap; } // Set "ConEmuArgs" and "ConEmuArgs2" ProcessConEmuArgsVar(); // Continue normal startup bRc = true; wrap: return bRc; }
INT_PTR CSetPgIntegr::PageDlgProc(HWND hDlg, UINT messg, WPARAM wParam, LPARAM lParam) { static bool bSkipCbSel = FALSE; INT_PTR iRc = 0; switch (messg) { case WM_NOTIFY: { LPNMHDR phdr = (LPNMHDR)lParam; if (phdr->code == TTN_GETDISPINFO) { return gpSetCls->ProcessTipHelp(hDlg, messg, wParam, lParam); } break; } case WM_INITDIALOG: { bSkipCbSel = true; PageDlgProc(hDlg, UM_RELOAD_HERE_LIST, UM_RELOAD_HERE_LIST, 0); //-- moved to "ComSpec" page //PageDlgProc(hDlg, UM_RELOAD_AUTORUN, UM_RELOAD_AUTORUN, 0); // Возвращает NULL, если строка пустая wchar_t* pszCurInside = GetDlgItemTextPtr(hDlg, cbInsideName); _ASSERTE((pszCurInside==NULL) || (*pszCurInside!=0)); wchar_t* pszCurHere = GetDlgItemTextPtr(hDlg, cbHereName); _ASSERTE((pszCurHere==NULL) || (*pszCurHere!=0)); wchar_t szIcon[MAX_PATH+32]; _wsprintf(szIcon, SKIPLEN(countof(szIcon)) L"%s,0", gpConEmu->ms_ConEmuExe); if (pszCurInside) { bSkipCbSel = false; PageDlgProc(hDlg, WM_COMMAND, MAKELONG(cbInsideName,CBN_SELCHANGE), 0); bSkipCbSel = true; } else { SetDlgItemText(hDlg, cbInsideName, L"ConEmu Inside"); SetDlgItemText(hDlg, tInsideConfig, L"shell"); SetDlgItemText(hDlg, tInsideShell, CONEMU_HERE_POSH); //SetDlgItemText(hDlg, tInsideIcon, szIcon); SetDlgItemText(hDlg, tInsideIcon, L"powershell.exe"); checkDlgButton(hDlg, cbInsideSyncDir, gpConEmu->mp_Inside && gpConEmu->mp_Inside->mb_InsideSynchronizeCurDir); SetDlgItemText(hDlg, tInsideSyncDir, L""); // Auto } if (pszCurHere) { bSkipCbSel = false; PageDlgProc(hDlg, WM_COMMAND, MAKELONG(cbHereName,CBN_SELCHANGE), 0); bSkipCbSel = true; } else { SetDlgItemText(hDlg, cbHereName, L"ConEmu Here"); SetDlgItemText(hDlg, tHereConfig, L""); SetDlgItemText(hDlg, tHereShell, CONEMU_HERE_CMD); SetDlgItemText(hDlg, tHereIcon, szIcon); } bSkipCbSel = false; SafeFree(pszCurInside); SafeFree(pszCurHere); } break; // WM_INITDIALOG case WM_COMMAND: switch (HIWORD(wParam)) { case BN_CLICKED: { WORD CB = LOWORD(wParam); switch (CB) { case cbInsideSyncDir: if (gpConEmu->mp_Inside) { gpConEmu->mp_Inside->mb_InsideSynchronizeCurDir = isChecked(hDlg, CB); } break; case bInsideRegister: case bInsideUnregister: ShellIntegration(hDlg, ShellIntgr_Inside, CB==bInsideRegister); PageDlgProc(hDlg, UM_RELOAD_HERE_LIST, UM_RELOAD_HERE_LIST, 0); if (CB==bInsideUnregister) PageDlgProc(hDlg, WM_COMMAND, MAKELONG(cbInsideName,CBN_SELCHANGE), 0); break; case bHereRegister: case bHereUnregister: ShellIntegration(hDlg, ShellIntgr_Here, CB==bHereRegister); PageDlgProc(hDlg, UM_RELOAD_HERE_LIST, UM_RELOAD_HERE_LIST, 0); if (CB==bHereUnregister) PageDlgProc(hDlg, WM_COMMAND, MAKELONG(cbHereName,CBN_SELCHANGE), 0); break; } } break; // BN_CLICKED case EN_CHANGE: { WORD EB = LOWORD(wParam); switch (EB) { case tInsideSyncDir: if (gpConEmu->mp_Inside) { SafeFree(gpConEmu->mp_Inside->ms_InsideSynchronizeCurDir); gpConEmu->mp_Inside->ms_InsideSynchronizeCurDir = GetDlgItemTextPtr(hDlg, tInsideSyncDir); } break; } } break; // EN_CHANGE case CBN_SELCHANGE: { WORD CB = LOWORD(wParam); switch (CB) { case cbInsideName: case cbHereName: if (!bSkipCbSel) { wchar_t *pszCfg = NULL, *pszIco = NULL, *pszFull = NULL, *pszDirSync = NULL; LPCWSTR pszCmd = NULL; INT_PTR iSel = SendDlgItemMessage(hDlg, CB, CB_GETCURSEL, 0,0); if (iSel >= 0) { INT_PTR iLen = SendDlgItemMessage(hDlg, CB, CB_GETLBTEXTLEN, iSel, 0); size_t cchMax = iLen+128; wchar_t* pszName = (wchar_t*)calloc(cchMax,sizeof(*pszName)); if ((iLen > 0) && pszName) { _wcscpy_c(pszName, cchMax, L"Directory\\shell\\"); SendDlgItemMessage(hDlg, CB, CB_GETLBTEXT, iSel, (LPARAM)(pszName+_tcslen(pszName))); HKEY hkShell = NULL; if (0 == RegOpenKeyEx(HKEY_CLASSES_ROOT, pszName, 0, KEY_READ, &hkShell)) { DWORD nType; DWORD nSize = MAX_PATH*2*sizeof(wchar_t); pszIco = (wchar_t*)calloc(nSize+2,1); if (0 != RegQueryValueEx(hkShell, L"Icon", NULL, &nType, (LPBYTE)pszIco, &nSize) || nType != REG_SZ) SafeFree(pszIco); HKEY hkCmd = NULL; if (0 == RegOpenKeyEx(hkShell, L"command", 0, KEY_READ, &hkCmd)) { DWORD nSize = MAX_PATH*8*sizeof(wchar_t); pszFull = (wchar_t*)calloc(nSize+2,1); if (0 != RegQueryValueEx(hkCmd, NULL, NULL, &nType, (LPBYTE)pszFull, &nSize) || nType != REG_SZ) { SafeFree(pszIco); } else { LPCWSTR psz = pszFull; LPCWSTR pszPrev = pszFull; CEStr szArg; while (0 == NextArg(&psz, szArg, &pszPrev)) { if (*szArg != L'/') continue; if ((lstrcmpi(szArg, L"/inside") == 0) || (lstrcmpi(szArg, L"/here") == 0) ) { // Nop } else if (lstrcmpni(szArg, L"/inside=", 8) == 0) { pszDirSync = lstrdup(szArg+8); // may be empty! } else if (lstrcmpi(szArg, L"/config") == 0) { if (0 != NextArg(&psz, szArg)) break; pszCfg = lstrdup(szArg); } else if (lstrcmpi(szArg, L"/dir") == 0) { if (0 != NextArg(&psz, szArg)) break; _ASSERTE(lstrcmpi(szArg, L"%1")==0); } else //if (lstrcmpi(szArg, L"/cmd") == 0) { if (lstrcmpi(szArg, L"/cmd") == 0) pszCmd = psz; else pszCmd = pszPrev; break; } } } RegCloseKey(hkCmd); } RegCloseKey(hkShell); } } SafeFree(pszName); } SetDlgItemText(hDlg, (CB==cbInsideName) ? tInsideConfig : tHereConfig, pszCfg ? pszCfg : L""); SetDlgItemText(hDlg, (CB==cbInsideName) ? tInsideShell : tHereShell, pszCmd ? pszCmd : L""); SetDlgItemText(hDlg, (CB==cbInsideName) ? tInsideIcon : tHereIcon, pszIco ? pszIco : L""); if (CB==cbInsideName) { SetDlgItemText(hDlg, tInsideSyncDir, pszDirSync ? pszDirSync : L""); checkDlgButton(hDlg, cbInsideSyncDir, (pszDirSync && *pszDirSync) ? BST_CHECKED : BST_UNCHECKED); } SafeFree(pszCfg); SafeFree(pszFull); SafeFree(pszIco); SafeFree(pszDirSync); } break; } } break; // CBN_SELCHANGE } // switch (HIWORD(wParam)) break; // WM_COMMAND case UM_RELOAD_HERE_LIST: if (wParam == UM_RELOAD_HERE_LIST) { HKEY hkDir = NULL; size_t cchCmdMax = 65535; wchar_t* pszCmd = (wchar_t*)calloc(cchCmdMax,sizeof(*pszCmd)); if (!pszCmd) break; // Возвращает NULL, если строка пустая wchar_t* pszCurInside = GetDlgItemTextPtr(hDlg, cbInsideName); _ASSERTE((pszCurInside==NULL) || (*pszCurInside!=0)); wchar_t* pszCurHere = GetDlgItemTextPtr(hDlg, cbHereName); _ASSERTE((pszCurHere==NULL) || (*pszCurHere!=0)); bool lbOldSkip = bSkipCbSel; bSkipCbSel = true; SendDlgItemMessage(hDlg, cbInsideName, CB_RESETCONTENT, 0, 0); SendDlgItemMessage(hDlg, cbHereName, CB_RESETCONTENT, 0, 0); if (0 == RegOpenKeyEx(HKEY_CLASSES_ROOT, L"Directory\\shell", 0, KEY_READ, &hkDir)) { for (DWORD i = 0; i < 512; i++) { wchar_t szName[MAX_PATH+32] = {}; DWORD cchMax = countof(szName) - 32; if (0 != RegEnumKeyEx(hkDir, i, szName, &cchMax, NULL, NULL, NULL, NULL)) break; wchar_t* pszSlash = szName + _tcslen(szName); wcscat_c(szName, L"\\command"); HKEY hkCmd = NULL; if (0 == RegOpenKeyEx(hkDir, szName, 0, KEY_READ, &hkCmd)) { DWORD cbMax = (cchCmdMax-2) * sizeof(*pszCmd); if (0 == RegQueryValueEx(hkCmd, NULL, NULL, NULL, (LPBYTE)pszCmd, &cbMax)) { pszCmd[cbMax>>1] = 0; *pszSlash = 0; LPCWSTR pszInside = StrStrI(pszCmd, L"/inside"); LPCWSTR pszConEmu = StrStrI(pszCmd, L"conemu"); if (pszConEmu) { SendDlgItemMessage(hDlg, pszInside ? cbInsideName : cbHereName, CB_ADDSTRING, 0, (LPARAM)szName); if ((pszInside ? pszCurInside : pszCurHere) == NULL) { if (pszInside) pszCurInside = lstrdup(szName); else pszCurHere = lstrdup(szName); } } } RegCloseKey(hkCmd); } } RegCloseKey(hkDir); }
// May comes from Task or ConEmu's -run switch // or from Setting\Environment page where one line is a single command (bAlone == true) bool CProcessEnvCmd::AddCommands(LPCWSTR asCommands, LPCWSTR* ppszEnd/*= NULL*/, bool bAlone /*= false*/, INT_PTR anBefore /*= -1*/) { bool bNew = false; LPCWSTR lsCmdLine = asCommands; CEStr lsSet, lsAmp, lsCmd; if (ppszEnd) *ppszEnd = asCommands; // Example: "set PATH=C:\Program Files;%PATH%" & set abc=def & cmd while (NextArg(&lsCmdLine, lsSet) == 0) { bool bTokenOk = false; wchar_t* lsNameVal = NULL; // It may contains only "set" or "alias" if was not quoted if ((lstrcmpi(lsSet, L"set") == 0) || (lstrcmpi(lsSet, L"alias") == 0)) { lsCmd.Set(lsSet); _ASSERTE(*lsCmdLine != L' '); // Now we shell get in lsSet "abc=def" token // Or to simplify usage, the rest of line is supported too, // so user may type in our Settings\Environment: // set V1=From Settings // instead of // set "V1=From Settings" bool bProcessed = false; if ((*lsCmdLine != L'"') || bAlone) { LPCWSTR pszAmp = bAlone ? NULL : wcschr(lsCmdLine, L'&'); if (!pszAmp) // No ampersand or bAlone? Use var value as the rest of line pszAmp = lsCmdLine + lstrlen(lsCmdLine); // Set tail pointer LPCWSTR pszValEnd = pszAmp; // Trim trailing spaces (only \x20) while ((pszValEnd > lsCmdLine) && (*(pszValEnd-1) == L' ')) pszValEnd--; // Trim possible leading/trailing quotes if (bAlone && (*lsCmdLine == L'"')) { lsCmdLine++; if (((pszValEnd-1) > lsCmdLine) && (*(pszValEnd-1) == L'"')) { _ASSERTE(*pszValEnd == 0); pszValEnd--; } } // OK, return it lsSet.Empty(); // to avoid debug asserts lsSet.Set(lsCmdLine, pszValEnd - lsCmdLine); lsCmdLine = SkipNonPrintable(pszAmp); // Leave possible '&' at pointer bProcessed = true; } // OK, lets get token like "name=var value" if (!bProcessed) { bProcessed = (NextArg(&lsCmdLine, lsSet) == 0); } if (bProcessed && (wcschr(lsSet, L'=') > lsSet.ms_Val)) { lsNameVal = lsSet.ms_Val; } } // Or full "set PATH=C:\Program Files;%PATH%" command (without quotes ATM) else if (lstrcmpni(lsSet, L"set ", 4) == 0) { lsCmd = L"set"; LPCWSTR psz = SkipNonPrintable(lsSet.ms_Val+4); if (wcschr(psz, L'=') > psz) { lsNameVal = (wchar_t*)psz; } } // Support "alias token=value" too else if (lstrcmpni(lsSet, L"alias ", 6) == 0) { lsCmd = L"alias"; LPCWSTR psz = SkipNonPrintable(lsSet.ms_Val+6); if (wcschr(psz, L'=') > psz) { lsNameVal = (wchar_t*)psz; } } // Process "chcp <cp>" too else if (lstrcmpi(lsSet, L"chcp") == 0) { lsCmd = L"chcp"; if (NextArg(&lsCmdLine, lsSet) == 0) { UINT nCP = GetCpFromString(lsSet); if (nCP > 0 && nCP <= 0xFFFF) { bTokenOk = true; _ASSERTE(lsNameVal == NULL); if (mp_CmdChCp) { SafeFree(mp_CmdChCp->pszName); mp_CmdChCp->pszName = lstrdup(lsSet); } else { bNew |= (NULL != Add(lsCmd, lsSet, NULL, anBefore)); } } } } // Change title without need of cmd.exe else if (lstrcmpi(lsSet, L"title") == 0) { lsCmd = L"title"; if (NextArg(&lsCmdLine, lsSet) == 0) { bTokenOk = true; _ASSERTE(lsNameVal == NULL); if (mp_CmdTitle) { SafeFree(mp_CmdTitle->pszName); mp_CmdTitle->pszName = lstrdup(lsSet); } else { bNew |= (NULL != Add(lsCmd, lsSet, NULL, anBefore)); } } } // Echo the "text" before console starts // Type the "text file" before console starts else if ((lstrcmpi(lsSet, L"echo") == 0) || (lstrcmpi(lsSet, L"type") == 0)) { lsCmd.Set(lsSet); // echo [-r] [-n] [-x] [-b] "String to echo" // type [-b] [-CP] "Path to text file to echo" CEStr lsSwitches; while (*lsCmdLine == L'-') { if (NextArg(&lsCmdLine, lsSet) != 0) break; lstrmerge(&lsSwitches.ms_Val, lsSwitches.IsEmpty() ? NULL : L" ", lsSet.ms_Val); } // Rest arguments are expected to be processed text or file CEStr lsAdd; while (NextArg(&lsCmdLine, lsSet) == 0) { bTokenOk = true; lstrmerge(&lsAdd.ms_Val, lsAdd.IsEmpty() ? NULL : L" ", lsSet.mb_Quoted ? L"\"" : NULL, lsSet.ms_Val, lsSet.mb_Quoted ? L"\"" : NULL); lsCmdLine = SkipNonPrintable(lsCmdLine); if (!*lsCmdLine || (*lsCmdLine == L'&') || (*lsCmdLine == L'|')) break; } if (!lsAdd.IsEmpty()) { bNew |= (NULL != Add(lsCmd, lsSwitches, lsAdd, anBefore)); } } else { lsCmd.Empty(); } // Well, known command was detected. What is next? if (lsNameVal || bTokenOk) { lsAmp.GetPosFrom(lsSet); if (NextArg(&lsCmdLine, lsAmp) != 0) { // End of command? Use may call only "set" without following app? Run simple "cmd" in that case _ASSERTE(lsCmdLine!=NULL && *lsCmdLine==0); bTokenOk = true; // And process SetEnvironmentVariable } else { if (lstrcmp(lsAmp, L"&") == 0) { // Only simple conveyer is supported! bTokenOk = true; // And process SetEnvironmentVariable } // Update last pointer (debug and asserts purposes) lsSet.GetPosFrom(lsAmp); } } if (!bTokenOk) { break; // Stop processing command line } else if (lsNameVal) { // And split name/value _ASSERTE(lsNameVal!=NULL); wchar_t* pszEq = wcschr(lsNameVal, L'='); if (!pszEq) { _ASSERTE(pszEq!=NULL); break; } *(pszEq++) = 0; bNew |= (NULL != Add(lsCmd, lsNameVal, pszEq ? pszEq : L"", anBefore)); } // Remember processed position if (ppszEnd) { *ppszEnd = lsCmdLine; } } // end of "while (NextArg(&lsCmdLine, lsSet) == 0)" // Fin if (ppszEnd && (!*ppszEnd || !**ppszEnd)) { static wchar_t szSimple[] = L"cmd"; *ppszEnd = szSimple; } return bNew; }