mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-08 14:07:00 -06:00
- considers valid windows escape sequences, like backslashes followed by quote-char as well as quote triples) - SplitCommandLine works now very similar to CommandLineToArgvW (but doesn't require dependency to shell32) Signed-off-by: Sergey G. Brester <serg.brester@sebres.de> Reviewed-by: Tino Reichardt <milky-zfs@mcmilk.de>
249 lines
5.2 KiB
C++
249 lines
5.2 KiB
C++
// CommandLineParser.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "CommandLineParser.h"
|
|
|
|
namespace NCommandLineParser {
|
|
|
|
bool SplitCommandLine(const UString &src, UString &dest1, UString &dest2)
|
|
{
|
|
unsigned qcount = 0, bcount = 0;
|
|
wchar_t c; const wchar_t *s = src.Ptr();
|
|
|
|
dest1.Empty();
|
|
|
|
while ((c = *s++) != 0)
|
|
{
|
|
switch (c)
|
|
{
|
|
case L'\\':
|
|
// a backslash - firstly add it as a regular char,
|
|
// we'll delete escaped later (if followed by quote):
|
|
dest1 += c;
|
|
bcount++;
|
|
break;
|
|
case L'"':
|
|
// check quote char is escaped:
|
|
if (!(bcount & 1))
|
|
{
|
|
// preceded by an even number of '\', this is half that
|
|
// number of '\' (delete bcount/2 chars):
|
|
dest1.DeleteFrom(dest1.Len() - bcount/2);
|
|
// count quote chars:
|
|
qcount++;
|
|
}
|
|
else
|
|
{
|
|
// preceded by an odd number of '\', this is half that
|
|
// number of '\' followed by an escaped '"':
|
|
dest1.DeleteFrom(dest1.Len() - bcount/2 - 1);
|
|
dest1 += L'"';
|
|
}
|
|
bcount = 0;
|
|
// now count the number of consecutive quotes (inclusive
|
|
// the quote that lead us here):
|
|
while (*s == L'"')
|
|
{
|
|
s++;
|
|
if (++qcount == 3)
|
|
{
|
|
dest1 += L'"';
|
|
qcount = 0;
|
|
}
|
|
}
|
|
if (qcount == 2)
|
|
qcount = 0;
|
|
break;
|
|
case L' ':
|
|
case L'\t':
|
|
// a space (end of arg or regular char):
|
|
if (!qcount)
|
|
{
|
|
// end of argument:
|
|
bcount = 0;
|
|
// skip to the next one:
|
|
while (isblank(*s)) { s++; };
|
|
goto done;
|
|
}
|
|
// no break - a space as regular char:
|
|
default:
|
|
// a regular character
|
|
dest1 += c;
|
|
bcount = 0;
|
|
}
|
|
}
|
|
s--; // back to NTS-zero char
|
|
done:
|
|
dest2 = s;
|
|
return (dest1.Len() || src[0]);
|
|
}
|
|
|
|
void SplitCommandLine(const UString &s, UStringVector &parts)
|
|
{
|
|
UString sTemp (s);
|
|
sTemp.Trim();
|
|
parts.Clear();
|
|
for (;;)
|
|
{
|
|
UString s1, s2;
|
|
if (SplitCommandLine(sTemp, s1, s2))
|
|
parts.Add(s1);
|
|
if (s2.IsEmpty())
|
|
break;
|
|
sTemp = s2;
|
|
}
|
|
}
|
|
|
|
|
|
static const char * const kStopSwitchParsing = "--";
|
|
|
|
static bool inline IsItSwitchChar(wchar_t c)
|
|
{
|
|
return (c == '-');
|
|
}
|
|
|
|
CParser::CParser():
|
|
_switches(NULL),
|
|
StopSwitchIndex(-1)
|
|
{
|
|
}
|
|
|
|
CParser::~CParser()
|
|
{
|
|
delete []_switches;
|
|
}
|
|
|
|
|
|
// if (s) contains switch then function updates switch structures
|
|
// out: true, if (s) is a switch
|
|
bool CParser::ParseString(const UString &s, const CSwitchForm *switchForms, unsigned numSwitches)
|
|
{
|
|
if (s.IsEmpty() || !IsItSwitchChar(s[0]))
|
|
return false;
|
|
|
|
unsigned pos = 1;
|
|
unsigned switchIndex = 0;
|
|
int maxLen = -1;
|
|
|
|
for (unsigned i = 0; i < numSwitches; i++)
|
|
{
|
|
const char * const key = switchForms[i].Key;
|
|
unsigned switchLen = MyStringLen(key);
|
|
if ((int)switchLen <= maxLen || pos + switchLen > s.Len())
|
|
continue;
|
|
if (IsString1PrefixedByString2_NoCase_Ascii((const wchar_t *)s + pos, key))
|
|
{
|
|
switchIndex = i;
|
|
maxLen = (int)switchLen;
|
|
}
|
|
}
|
|
|
|
if (maxLen < 0)
|
|
{
|
|
ErrorMessage = "Unknown switch:";
|
|
return false;
|
|
}
|
|
|
|
pos += (unsigned)maxLen;
|
|
|
|
CSwitchResult &sw = _switches[switchIndex];
|
|
const CSwitchForm &form = switchForms[switchIndex];
|
|
|
|
if (!form.Multi && sw.ThereIs)
|
|
{
|
|
ErrorMessage = "Multiple instances for switch:";
|
|
return false;
|
|
}
|
|
|
|
sw.ThereIs = true;
|
|
|
|
const unsigned rem = s.Len() - pos;
|
|
if (rem < form.MinLen)
|
|
{
|
|
ErrorMessage = "Too short switch:";
|
|
return false;
|
|
}
|
|
|
|
sw.WithMinus = false;
|
|
sw.PostCharIndex = -1;
|
|
|
|
switch (form.Type)
|
|
{
|
|
case NSwitchType::kMinus:
|
|
if (rem == 1)
|
|
{
|
|
sw.WithMinus = (s[pos] == '-');
|
|
if (sw.WithMinus)
|
|
return true;
|
|
ErrorMessage = "Incorrect switch postfix:";
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case NSwitchType::kChar:
|
|
if (rem == 1)
|
|
{
|
|
wchar_t c = s[pos];
|
|
if (c <= 0x7F)
|
|
{
|
|
sw.PostCharIndex = FindCharPosInString(form.PostCharSet, (char)c);
|
|
if (sw.PostCharIndex >= 0)
|
|
return true;
|
|
}
|
|
ErrorMessage = "Incorrect switch postfix:";
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case NSwitchType::kString:
|
|
{
|
|
sw.PostStrings.Add(s.Ptr(pos));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (pos != s.Len())
|
|
{
|
|
ErrorMessage = "Too long switch:";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CParser::ParseStrings(const CSwitchForm *switchForms, unsigned numSwitches, const UStringVector &commandStrings)
|
|
{
|
|
StopSwitchIndex = -1;
|
|
ErrorMessage.Empty();
|
|
ErrorLine.Empty();
|
|
NonSwitchStrings.Clear();
|
|
delete []_switches;
|
|
_switches = NULL;
|
|
_switches = new CSwitchResult[numSwitches];
|
|
|
|
FOR_VECTOR (i, commandStrings)
|
|
{
|
|
const UString &s = commandStrings[i];
|
|
if (StopSwitchIndex < 0)
|
|
{
|
|
if (s.IsEqualTo(kStopSwitchParsing))
|
|
{
|
|
StopSwitchIndex = (int)NonSwitchStrings.Size();
|
|
continue;
|
|
}
|
|
if (!s.IsEmpty() && IsItSwitchChar(s[0]))
|
|
{
|
|
if (ParseString(s, switchForms, numSwitches))
|
|
continue;
|
|
ErrorLine = s;
|
|
return false;
|
|
}
|
|
}
|
|
NonSwitchStrings.Add(s);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
}
|