Exemplo n.º 1
0
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);
	}
}
Exemplo n.º 2
0
//-------
// ^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
}
Exemplo n.º 3
0
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;
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
//-------
// ^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 ;
}
Exemplo n.º 6
0
//-------
// ^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 ;
}
Exemplo n.º 7
0
//-------
// ^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 ;
}
Exemplo n.º 8
0
//-------
// ^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 ;
}
Exemplo n.º 9
0
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;
}
Exemplo n.º 10
0
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;
}
Exemplo n.º 11
0
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;
}
Exemplo n.º 12
0
//-------
// ^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 ;
}
Exemplo n.º 13
0
// 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;
}
Exemplo n.º 14
0
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;
}
Exemplo n.º 15
0
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;
}
Exemplo n.º 16
0
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;
}
Exemplo n.º 17
0
// 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;
}
Exemplo n.º 18
0
// Возвращает 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;
}
Exemplo n.º 19
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;
}