Skip to content

Commit

Permalink
gh-2095: Fix escaping of postponed GuiMacro scripts.
Browse files Browse the repository at this point in the history
  • Loading branch information
Maximus5 committed Apr 19, 2020
1 parent ed64425 commit 97d7cc7
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 47 deletions.
19 changes: 8 additions & 11 deletions src/ConEmu/MacroImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,8 @@ wchar_t* GuiMacro::AsString()
LPWSTR pszDst = pszArgs[i];
// Start
*(pszDst++) = L'"';
// Loop
while (*pszSrc)
{
EscapeChar(true, pszSrc, pszDst);
}
// Value
EscapeString(pszSrc, pszDst);
// Done
*(pszDst++) = L'"'; *(pszDst++) = 0;
cchTotal += _tcslen(pszArgs[i])+1;
Expand Down Expand Up @@ -632,7 +629,7 @@ LPWSTR GetNextString(LPWSTR& rsArguments, LPWSTR& rsString, bool bColonDelim, bo
LPWSTR pszDst = rsString;
while (*pszSrc && (*pszSrc != L'"'))
{
EscapeChar(false, pszSrc, pszDst);
UnescapeChar(pszSrc, pszDst);
}
_ASSERTE((*pszSrc == L'"') || (*pszSrc == 0));
rsArguments = (wchar_t*)((*pszSrc == L'"') ? (pszSrc+1) : pszSrc);
Expand All @@ -659,21 +656,21 @@ LPWSTR GetNextString(LPWSTR& rsArguments, LPWSTR& rsString, bool bColonDelim, bo
{
DEBUGTEST(LPCWSTR pszStart = pszSrc);
pszTemp = szTemp;
EscapeChar(false, pszSrc, pszTemp);
EscapeChar(false, pszSrc, pszTemp);
UnescapeChar(pszSrc, pszTemp);
UnescapeChar(pszSrc, pszTemp);
_ASSERTE((pszTemp==(szTemp+2)) && (pszSrc==(pszStart+3) || pszSrc==(pszStart+4)));
pszTemp = szTemp;
LPCWSTR pszReent = szTemp;
EscapeChar(false, pszReent, pszDst);
UnescapeChar(pszReent, pszDst);
}
else
{
EscapeChar(false, pszSrc, pszTemp);
UnescapeChar(pszSrc, pszTemp);
}
}
else
{
EscapeChar(false, pszSrc, pszDst);
UnescapeChar(pszSrc, pszDst);
}
}
_ASSERTE((*pszSrc == L'"') || (*pszSrc == 0));
Expand Down
15 changes: 0 additions & 15 deletions src/ConEmu/helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,21 +302,6 @@ bool FixDirEndSlash(wchar_t* rsPath)
return false;
}

// TODO: Optimize: Now pszDst must be (4x len in maximum for "\xFF" form) for bSet==true
void EscapeChar(bool bSet, LPCWSTR& pszSrc, LPWSTR& pszDst)
{
if (bSet)
{
// Set escapes: wchar(13) --> "\\r"
EscapeChar(pszSrc, pszDst);
}
else
{
// Remove escapes: "\\r" --> wchar(13), etc.
UnescapeChar(pszSrc, pszDst);
}
}

bool isKey(DWORD wp,DWORD vk)
{
bool bEq = ((wp==vk)
Expand Down
2 changes: 0 additions & 2 deletions src/ConEmu/helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ LPCWSTR MakeWinPath(LPCWSTR asAnyPath, LPCWSTR pszMntPrefix, CEStr& szWinPath);
wchar_t* MakeStraightSlashPath(LPCWSTR asWinPath);
bool FixDirEndSlash(wchar_t* rsPath);

void EscapeChar(bool bSet, LPCWSTR& pszSrc, LPWSTR& pszDst);

bool isKey(DWORD wp,DWORD vk);

// pszWords - '|'separated
Expand Down
1 change: 1 addition & 0 deletions src/Tests.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,7 @@
<ClCompile Include="common\MSetter.cpp" />
<ClCompile Include="common\MStrDup.cpp" />
<ClCompile Include="common\MStrEsc.cpp" />
<ClCompile Include="common\MStrEsc_test.cpp" />
<ClCompile Include="common\MStrSafe.cpp" />
<ClCompile Include="common\MTimer.cpp" />
<ClCompile Include="common\MToolTip.cpp" />
Expand Down
3 changes: 3 additions & 0 deletions src/Tests.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,9 @@
<ClCompile Include="common\MStrEsc.cpp">
<Filter>common</Filter>
</ClCompile>
<ClCompile Include="common\MStrEsc_test.cpp">
<Filter>tests</Filter>
</ClCompile>
<ClCompile Include="common\MStrSafe.cpp">
<Filter>common</Filter>
</ClCompile>
Expand Down
89 changes: 72 additions & 17 deletions src/common/MStrEsc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,21 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

/// Set escapes: wchar(13) --> "\\r", etc.
/// pszSrc and pszDst must point to different memory blocks
/// pszDst must have at least 5 bytes available (four "\\xFF" and final "\0")
void EscapeChar(LPCWSTR& pszSrc, LPWSTR& pszDst)
/// pszDst must have at least 5 wchars available (four "\\xFF" and final "\0")
bool EscapeChar(LPCWSTR& pszSrc, LPWSTR& pszDst)
{
_ASSERTE(pszSrc && pszDst);
_ASSERTE(pszSrc != pszDst);

LPCWSTR pszCtrl = L"rntae\\\"";
if (!pszSrc || !pszDst)
{
_ASSERTE(pszSrc && pszDst);
return false;
}
if (pszSrc == pszDst)
{
_ASSERTE(pszSrc != pszDst);
return false;
}

wchar_t wc = *pszSrc;
const wchar_t wc = *pszSrc;
switch (wc)
{
case L'"': // 34
Expand All @@ -49,10 +55,9 @@ void EscapeChar(LPCWSTR& pszSrc, LPWSTR& pszDst)
pszSrc++;
break;
case L'\\': // 92
*(pszDst++) = L'\\';
*(pszDst++) = L'\\';
pszSrc++;
if (!*pszSrc || !wcschr(pszCtrl, *pszSrc))
*(pszDst++) = L'\\';
break;
case L'\r': // 13
*(pszDst++) = L'\\';
Expand Down Expand Up @@ -102,12 +107,18 @@ void EscapeChar(LPCWSTR& pszSrc, LPWSTR& pszDst)
}
*(pszDst++) = *(pszSrc++);
}

return true;
}

/// Remove escapes: "\\r" --> wchar(13), etc.
void UnescapeChar(LPCWSTR& pszSrc, LPWSTR& pszDst)
bool UnescapeChar(LPCWSTR& pszSrc, LPWSTR& pszDst)
{
_ASSERTE(pszSrc && pszDst);
if (!pszSrc || !pszDst)
{
_ASSERTE(pszSrc && pszDst);
return false;
}

if (*pszSrc == L'\\')
{
Expand Down Expand Up @@ -171,16 +182,60 @@ void UnescapeChar(LPCWSTR& pszSrc, LPWSTR& pszDst)
{
*(pszDst++) = *(pszSrc++);
}

return true;
}

bool CheckStrForSpecials(LPCWSTR pszStr, bool* pbSlash /*= NULL*/, bool* pbOthers /*= NULL*/)
/// Set escapes: wchar(13) --> "\\r", etc.
/// pszSrc and pszDst must point to different memory blocks
/// pszDst must have at least 1+4*len(pszSrc) wchars available (four "\\xFF" and final "\0")
bool EscapeString(LPCWSTR& pszSrc, LPWSTR& pszDst)
{
if (!pszSrc || !pszDst)
{
_ASSERTE(pszSrc && pszDst);
return false;
}
if (pszSrc == pszDst)
{
_ASSERTE(pszSrc != pszDst);
return false;
}

while (*pszSrc)
{
EscapeChar(pszSrc, pszDst);
}

*pszDst = 0;

return true;
}

bool UnescapeString(LPCWSTR& pszSrc, LPWSTR& pszDst)
{
// Intended for GuiMacro representation
// Если строка содержит из спец-символов ТОЛЬКО "\" (пути)
// то ее удобнее показывать как "Verbatim", иначе - C-String
// Однозначно показывать как C-String нужно те строки, которые
// содержат переводы строк, Esc, табуляции и пр.
if (!pszSrc || !pszDst)
{
_ASSERTE(pszSrc && pszDst);
return false;
}

while (*pszSrc)
{
UnescapeChar(pszSrc, pszDst);
}

*pszDst = 0;

return true;
}

/// Intended for GuiMacro representation
/// If *pszStr* doesn't contain special symbols with ONLY exception of "\" (paths)
/// it's more convenient to represent it as *Verbatim* string, otherwise - *C-String*.
/// Always show as *C-String* those strings, which contain CRLF, Esc, tabs etc.
bool CheckStrForSpecials(LPCWSTR pszStr, bool* pbSlash /*= NULL*/, bool* pbOthers /*= NULL*/)
{
bool bSlash = false, bOthers = false;

if (pszStr && *pszStr)
Expand Down
7 changes: 5 additions & 2 deletions src/common/MStrEsc.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "defines.h"

void EscapeChar(LPCWSTR& pszSrc, LPWSTR& pszDst);
void UnescapeChar(LPCWSTR& pszSrc, LPWSTR& pszDst);
bool EscapeChar(LPCWSTR& pszSrc, LPWSTR& pszDst);
bool UnescapeChar(LPCWSTR& pszSrc, LPWSTR& pszDst);

bool EscapeString(LPCWSTR& pszSrc, LPWSTR& pszDst);
bool UnescapeString(LPCWSTR& pszSrc, LPWSTR& pszDst);

bool CheckStrForSpecials(LPCWSTR pszStr, bool* pbSlash = NULL, bool* pbOthers = NULL);
101 changes: 101 additions & 0 deletions src/common/MStrEsc_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@

/*
Copyright (c) 2009-present Maximus5
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#define HIDE_USE_EXCEPTION_INFO
#define SHOWDEBUGSTR

#include "defines.h"
#include "../UnitTests/gtest.h"

#include "MStrEsc.h"

TEST(MStrEsc, CheckStrForSpecials)
{
struct StringChecks { LPCWSTR pszStr; bool result, slash, others; };
const StringChecks tests[] = {
{L"", false, false, false},
{L"empty", false, false, false},
{L"c:\\dir\\file", true, true, false},
{L"c:/dir/file", false, false, false},
{L"c:\\dir\\file\n", true, true, true},
{L"line\n", true, false, true},
};
for (size_t i = 0; i < std::size(tests); ++i)
{
bool slash = false, others = false;
EXPECT_EQ(CheckStrForSpecials(tests[i].pszStr, &slash, &others), tests[i].result);
EXPECT_EQ(slash, tests[i].slash);
EXPECT_EQ(others, tests[i].others);
}

EXPECT_EQ(CheckStrForSpecials(nullptr, nullptr, nullptr), false);
}

TEST(MStrEsc, EscapeString)
{
struct EscapeChecks { LPCWSTR pszSrc; LPCWSTR pszDst; };
const EscapeChecks tests[] = {
{L"", L""},
{L"no escapes", L"no escapes"},
{L"1\n2\t3\"4\\e", L"1\\n2\\t3\\\"4\\\\e"},
{L"hello \\e tom", L"hello \\\\e tom"},
};
for (size_t i = 0; i < std::size(tests); ++i)
{
wchar_t szBuffer[128] = L"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
LPCWSTR pszSrc = tests[i].pszSrc;
LPWSTR pszDst = szBuffer;
EXPECT_EQ(EscapeString(pszSrc, pszDst), true);
EXPECT_STREQ(szBuffer, tests[i].pszDst);
wchar_t szReverted[128] = L"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
pszSrc = szBuffer;
pszDst = szReverted;
EXPECT_EQ(UnescapeString(pszSrc, pszDst), true);
EXPECT_STREQ(szReverted, tests[i].pszSrc);
}
}

TEST(MStrEsc, UnescapeString)
{
struct UnescapeChecks { LPCWSTR pszSrc; LPCWSTR pszDst; };
const UnescapeChecks tests[] = {
{L"", L""},
{L"no escapes", L"no escapes"},
{L"1\\n2\\t3\\\"4\\\\e", L"1\n2\t3\"4\\e"},
{L"hello \\\\e tom", L"hello \\e tom"},
{L"hello \\e tom", L"hello \x1B tom"},
};
for (size_t i = 0; i < std::size(tests); ++i)
{
wchar_t szBuffer[128] = L"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
LPCWSTR pszSrc = tests[i].pszSrc;
LPWSTR pszDst = szBuffer;
EXPECT_EQ(UnescapeString(pszSrc, pszDst), true);
EXPECT_STREQ(szBuffer, tests[i].pszDst);
}
}

0 comments on commit 97d7cc7

Please sign in to comment.