mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-06 07:14:55 -06:00
Signed-off-by: Sergey G. Brester <info@sebres.de> Reviewed-by: Tino Reichardt <milky-7zip@mcmilk.de>
265 lines
5.6 KiB
C++
265 lines
5.6 KiB
C++
// CommandLineParser.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "CommandLineParser.h"
|
|
|
|
namespace NCommandLineParser {
|
|
|
|
static const wchar_t * _SplitCommandLine(const wchar_t* s, UString &dest)
|
|
{
|
|
unsigned qcount = 0, bcount = 0;
|
|
wchar_t c; const wchar_t *f, *b;
|
|
|
|
dest.Empty();
|
|
|
|
// skip spaces:
|
|
while (isblank(*s)) { s++; };
|
|
b = f = s;
|
|
|
|
while ((c = *s++) != 0)
|
|
{
|
|
switch (c)
|
|
{
|
|
case L'\\':
|
|
// a backslash - count them up to quote-char or regular char
|
|
bcount++;
|
|
break;
|
|
case L'"':
|
|
// check quote char is escaped:
|
|
if (!(bcount & 1))
|
|
{
|
|
// preceded by an even number of '\', this is half that
|
|
// number of '\':
|
|
dest.AddFrom(f, (unsigned)(s - f - bcount/2 - 1)); f = s;
|
|
// count quote chars:
|
|
qcount++;
|
|
}
|
|
else
|
|
{
|
|
// preceded by an odd number of '\', this is half that
|
|
// number of '\' followed by an escaped '"':
|
|
dest.AddFrom(f, (unsigned)(s - f - bcount/2 - 2)); f = s;
|
|
dest += L'"';
|
|
}
|
|
bcount = 0;
|
|
// now count the number of consecutive quotes (inclusive
|
|
// the quote that lead us here):
|
|
while (*s == L'"')
|
|
{
|
|
s++;
|
|
if (++qcount == 3)
|
|
{
|
|
dest += L'"';
|
|
qcount = 0;
|
|
}
|
|
}
|
|
f = s;
|
|
if (qcount == 2)
|
|
qcount = 0;
|
|
break;
|
|
case L' ':
|
|
case L'\t':
|
|
// a space (end of arg or regular char):
|
|
if (!qcount)
|
|
{
|
|
// end of argument:
|
|
dest.AddFrom(f, (unsigned)(s - f - 1)); f = s;
|
|
// skip to the next one:
|
|
while (isblank(*s)) { s++; };
|
|
bcount = 0;
|
|
goto done;
|
|
}
|
|
// no break - a space as regular char:
|
|
default:
|
|
// a regular character, reset backslash counter
|
|
bcount = 0;
|
|
}
|
|
}
|
|
s--; // back to NTS-zero char
|
|
dest.AddFrom(f, (unsigned)(s - f));
|
|
done:
|
|
// remaining part if argument was found, otherwise NULL:
|
|
return (dest.Len() || *b) ? s : NULL;
|
|
}
|
|
|
|
bool SplitCommandLine(const UString& src, UString& dest1, UString& dest2)
|
|
{
|
|
const wchar_t *s = src.Ptr();
|
|
s = _SplitCommandLine(s, dest1);
|
|
if (s) {
|
|
dest2 = s;
|
|
return true;
|
|
} else {
|
|
dest2.Empty();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void SplitCommandLine(const UString &src, UStringVector &parts)
|
|
{
|
|
const wchar_t *s = src.Ptr();
|
|
parts.Clear();
|
|
for (;;)
|
|
{
|
|
UString s1;
|
|
s = _SplitCommandLine(s, s1);
|
|
if (s)
|
|
parts.Add(s1);
|
|
if (!s || !*s)
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
}
|