Added CmdLineParser, proper parsing of command line parameters, and -v parameter for verbosity.

This commit is contained in:
Adam Sawicki 2018-08-13 13:13:13 +02:00
parent db1f73fee4
commit 652ac27547
3 changed files with 616 additions and 15 deletions

View File

@ -1,5 +1,436 @@
#include "Common.h"
////////////////////////////////////////////////////////////////////////////////
// class CmdLineParser
bool CmdLineParser::ReadNextArg(std::string *OutArg)
{
if (m_argv != NULL)
{
if (m_ArgIndex >= (size_t)m_argc) return false;
*OutArg = m_argv[m_ArgIndex];
m_ArgIndex++;
return true;
}
else
{
if (m_ArgIndex >= m_CmdLineLength) return false;
OutArg->clear();
bool InsideQuotes = false;
while (m_ArgIndex < m_CmdLineLength)
{
char Ch = m_CmdLine[m_ArgIndex];
if (Ch == '\\')
{
bool FollowedByQuote = false;
size_t BackslashCount = 1;
size_t TmpIndex = m_ArgIndex + 1;
while (TmpIndex < m_CmdLineLength)
{
char TmpCh = m_CmdLine[TmpIndex];
if (TmpCh == '\\')
{
BackslashCount++;
TmpIndex++;
}
else if (TmpCh == '"')
{
FollowedByQuote = true;
break;
}
else
break;
}
if (FollowedByQuote)
{
if (BackslashCount % 2 == 0)
{
for (size_t i = 0; i < BackslashCount / 2; i++)
*OutArg += '\\';
m_ArgIndex += BackslashCount + 1;
InsideQuotes = !InsideQuotes;
}
else
{
for (size_t i = 0; i < BackslashCount / 2; i++)
*OutArg += '\\';
*OutArg += '"';
m_ArgIndex += BackslashCount + 1;
}
}
else
{
for (size_t i = 0; i < BackslashCount; i++)
*OutArg += '\\';
m_ArgIndex += BackslashCount;
}
}
else if (Ch == '"')
{
InsideQuotes = !InsideQuotes;
m_ArgIndex++;
}
else if (isspace(Ch))
{
if (InsideQuotes)
{
*OutArg += Ch;
m_ArgIndex++;
}
else
{
m_ArgIndex++;
break;
}
}
else
{
*OutArg += Ch;
m_ArgIndex++;
}
}
while (m_ArgIndex < m_CmdLineLength && isspace(m_CmdLine[m_ArgIndex]))
m_ArgIndex++;
return true;
}
}
CmdLineParser::SHORT_OPT * CmdLineParser::FindShortOpt(char Opt)
{
for (size_t i = 0; i < m_ShortOpts.size(); i++)
if (m_ShortOpts[i].Opt == Opt)
return &m_ShortOpts[i];
return NULL;
}
CmdLineParser::LONG_OPT * CmdLineParser::FindLongOpt(const std::string &Opt)
{
for (size_t i = 0; i < m_LongOpts.size(); i++)
if (m_LongOpts[i].Opt == Opt)
return &m_LongOpts[i];
return NULL;
}
CmdLineParser::CmdLineParser(int argc, char **argv) :
m_argv(argv),
m_CmdLine(NULL),
m_argc(argc),
m_CmdLineLength(0),
m_ArgIndex(1),
m_InsideMultioption(false),
m_LastArgIndex(0),
m_LastOptId(0)
{
assert(argc > 0);
assert(argv != NULL);
}
CmdLineParser::CmdLineParser(const char *CmdLine) :
m_argv(NULL),
m_CmdLine(CmdLine),
m_argc(0),
m_ArgIndex(0),
m_InsideMultioption(false),
m_LastArgIndex(0),
m_LastOptId(0)
{
assert(CmdLine != NULL);
m_CmdLineLength = strlen(m_CmdLine);
while (m_ArgIndex < m_CmdLineLength && isspace(m_CmdLine[m_ArgIndex]))
m_ArgIndex++;
}
void CmdLineParser::RegisterOpt(uint32_t Id, char Opt, bool Parameter)
{
assert(Opt != '\0');
m_ShortOpts.push_back(SHORT_OPT(Id, Opt, Parameter));
}
void CmdLineParser::RegisterOpt(uint32_t Id, const std::string &Opt, bool Parameter)
{
assert(!Opt.empty());
m_LongOpts.push_back(LONG_OPT(Id, Opt, Parameter));
}
CmdLineParser::RESULT CmdLineParser::ReadNext()
{
if (m_InsideMultioption)
{
assert(m_LastArgIndex < m_LastArg.length());
SHORT_OPT *so = FindShortOpt(m_LastArg[m_LastArgIndex]);
if (so == NULL)
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
if (so->Parameter)
{
if (m_LastArg.length() == m_LastArgIndex+1)
{
if (!ReadNextArg(&m_LastParameter))
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
m_InsideMultioption = false;
m_LastOptId = so->Id;
return CmdLineParser::RESULT_OPT;
}
else if (m_LastArg[m_LastArgIndex+1] == '=')
{
m_InsideMultioption = false;
m_LastParameter = m_LastArg.substr(m_LastArgIndex+2);
m_LastOptId = so->Id;
return CmdLineParser::RESULT_OPT;
}
else
{
m_InsideMultioption = false;
m_LastParameter = m_LastArg.substr(m_LastArgIndex+1);
m_LastOptId = so->Id;
return CmdLineParser::RESULT_OPT;
}
}
else
{
if (m_LastArg.length() == m_LastArgIndex+1)
{
m_InsideMultioption = false;
m_LastParameter.clear();
m_LastOptId = so->Id;
return CmdLineParser::RESULT_OPT;
}
else
{
m_LastArgIndex++;
m_LastParameter.clear();
m_LastOptId = so->Id;
return CmdLineParser::RESULT_OPT;
}
}
}
else
{
if (!ReadNextArg(&m_LastArg))
{
m_LastParameter.clear();
m_LastOptId = 0;
return CmdLineParser::RESULT_END;
}
if (!m_LastArg.empty() && m_LastArg[0] == '-')
{
if (m_LastArg.length() > 1 && m_LastArg[1] == '-')
{
size_t EqualIndex = m_LastArg.find('=', 2);
if (EqualIndex != std::string::npos)
{
LONG_OPT *lo = FindLongOpt(m_LastArg.substr(2, EqualIndex-2));
if (lo == NULL || lo->Parameter == false)
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
m_LastParameter = m_LastArg.substr(EqualIndex+1);
m_LastOptId = lo->Id;
return CmdLineParser::RESULT_OPT;
}
else
{
LONG_OPT *lo = FindLongOpt(m_LastArg.substr(2));
if (lo == NULL)
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
if (lo->Parameter)
{
if (!ReadNextArg(&m_LastParameter))
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
}
else
m_LastParameter.clear();
m_LastOptId = lo->Id;
return CmdLineParser::RESULT_OPT;
}
}
else
{
if (m_LastArg.length() < 2)
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
SHORT_OPT *so = FindShortOpt(m_LastArg[1]);
if (so == NULL)
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
if (so->Parameter)
{
if (m_LastArg.length() == 2)
{
if (!ReadNextArg(&m_LastParameter))
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
m_LastOptId = so->Id;
return CmdLineParser::RESULT_OPT;
}
else if (m_LastArg[2] == '=')
{
m_LastParameter = m_LastArg.substr(3);
m_LastOptId = so->Id;
return CmdLineParser::RESULT_OPT;
}
else
{
m_LastParameter = m_LastArg.substr(2);
m_LastOptId = so->Id;
return CmdLineParser::RESULT_OPT;
}
}
else
{
if (m_LastArg.length() == 2)
{
m_LastParameter.clear();
m_LastOptId = so->Id;
return CmdLineParser::RESULT_OPT;
}
else
{
m_InsideMultioption = true;
m_LastArgIndex = 2;
m_LastParameter.clear();
m_LastOptId = so->Id;
return CmdLineParser::RESULT_OPT;
}
}
}
}
else if (!m_LastArg.empty() && m_LastArg[0] == '/')
{
size_t EqualIndex = m_LastArg.find('=', 1);
if (EqualIndex != std::string::npos)
{
if (EqualIndex == 2)
{
SHORT_OPT *so = FindShortOpt(m_LastArg[1]);
if (so != NULL)
{
if (so->Parameter == false)
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
m_LastParameter = m_LastArg.substr(EqualIndex+1);
m_LastOptId = so->Id;
return CmdLineParser::RESULT_OPT;
}
}
LONG_OPT *lo = FindLongOpt(m_LastArg.substr(1, EqualIndex-1));
if (lo == NULL || lo->Parameter == false)
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
m_LastParameter = m_LastArg.substr(EqualIndex+1);
m_LastOptId = lo->Id;
return CmdLineParser::RESULT_OPT;
}
else
{
if (m_LastArg.length() == 2)
{
SHORT_OPT *so = FindShortOpt(m_LastArg[1]);
if (so != NULL)
{
if (so->Parameter)
{
if (!ReadNextArg(&m_LastParameter))
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
}
else
m_LastParameter.clear();
m_LastOptId = so->Id;
return CmdLineParser::RESULT_OPT;
}
}
LONG_OPT *lo = FindLongOpt(m_LastArg.substr(1));
if (lo == NULL)
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
if (lo->Parameter)
{
if (!ReadNextArg(&m_LastParameter))
{
m_LastOptId = 0;
m_LastParameter.clear();
return CmdLineParser::RESULT_ERROR;
}
}
else
m_LastParameter.clear();
m_LastOptId = lo->Id;
return CmdLineParser::RESULT_OPT;
}
}
else
{
m_LastOptId = 0;
m_LastParameter = m_LastArg;
return CmdLineParser::RESULT_PARAMETER;
}
}
}
uint32_t CmdLineParser::GetOptId()
{
return m_LastOptId;
}
const std::string & CmdLineParser::GetParameter()
{
return m_LastParameter;
}
////////////////////////////////////////////////////////////////////////////////
// Glolals
/*
void SetConsoleColor(CONSOLE_COLOR color)

View File

@ -39,6 +39,67 @@ static inline T align_up(T val, T align)
return (val + align - 1) / align * align;
}
class CmdLineParser
{
public:
enum RESULT
{
RESULT_OPT,
RESULT_PARAMETER,
RESULT_END,
RESULT_ERROR,
};
CmdLineParser(int argc, char **argv);
CmdLineParser(const char *CmdLine);
void RegisterOpt(uint32_t Id, char Opt, bool Parameter);
void RegisterOpt(uint32_t Id, const std::string &Opt, bool Parameter);
RESULT ReadNext();
uint32_t GetOptId();
const std::string & GetParameter();
private:
struct SHORT_OPT
{
uint32_t Id;
char Opt;
bool Parameter;
SHORT_OPT(uint32_t Id, char Opt, bool Parameter) : Id(Id), Opt(Opt), Parameter(Parameter) { }
};
struct LONG_OPT
{
uint32_t Id;
std::string Opt;
bool Parameter;
LONG_OPT(uint32_t Id, std::string Opt, bool Parameter) : Id(Id), Opt(Opt), Parameter(Parameter) { }
};
char **m_argv;
const char *m_CmdLine;
int m_argc;
size_t m_CmdLineLength;
size_t m_ArgIndex;
bool ReadNextArg(std::string *OutArg);
std::vector<SHORT_OPT> m_ShortOpts;
std::vector<LONG_OPT> m_LongOpts;
SHORT_OPT * FindShortOpt(char Opt);
LONG_OPT * FindLongOpt(const std::string &Opt);
bool m_InsideMultioption;
std::string m_LastArg;
size_t m_LastArgIndex;
uint32_t m_LastOptId;
std::string m_LastParameter;
};
/*
class RandomNumberGenerator
{

View File

@ -30,11 +30,31 @@ static const int RESULT_ERROR_SOURCE_FILE = -2;
static const int RESULT_ERROR_FORMAT = -3;
static const int RESULT_ERROR_VULKAN = -4;
enum CMD_LINE_OPT
{
CMD_LINE_OPT_VERBOSITY,
};
static enum class VERBOSITY
{
MINIMUM = 0,
DEFAULT,
MAXIMUM,
COUNT,
} g_Verbosity = VERBOSITY::DEFAULT;
static std::string g_FilePath;
struct StrRange
{
const char* beg;
const char* end;
StrRange() { }
StrRange(const char* beg, const char* end) : beg(beg), end(end) { }
explicit StrRange(const char* sz) : beg(sz), end(sz + strlen(sz)) { }
explicit StrRange(const std::string& s) : beg(s.data()), end(s.data() + s.length()) { }
size_t length() const { return end - beg; }
};
@ -286,7 +306,7 @@ private:
void Destroy(const Allocation& alloc);
// Increments warning counter. Returns true if warning message should be printed.
bool IssueWarning() { return m_WarningCount++ < MAX_WARNINGS_TO_SHOW; }
bool IssueWarning();
int InitVulkan();
void FinalizeVulkan();
@ -318,11 +338,14 @@ int Player::Init()
Player::~Player()
{
if(g_Verbosity > VERBOSITY::MINIMUM)
{
PrintStats();
}
FinalizeVulkan();
if(m_WarningCount > MAX_WARNINGS_TO_SHOW)
if(g_Verbosity < VERBOSITY::MAXIMUM && m_WarningCount > MAX_WARNINGS_TO_SHOW)
printf("WARNING: %zu more warnings not shown.\n", m_WarningCount - MAX_WARNINGS_TO_SHOW);
}
@ -421,9 +444,25 @@ void Player::Destroy(const Allocation& alloc)
vmaFreeMemory(m_Allocator, alloc.allocation);
}
bool Player::IssueWarning()
{
if(g_Verbosity < VERBOSITY::MAXIMUM)
{
return m_WarningCount++ < MAX_WARNINGS_TO_SHOW;
}
else
{
++m_WarningCount;
return true;
}
}
int Player::InitVulkan()
{
if(g_Verbosity > VERBOSITY::MINIMUM)
{
printf("Initializing Vulkan...\n");
}
uint32_t instanceLayerPropCount = 0;
VkResult res = vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr);
@ -984,14 +1023,24 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit)
static void PrintCommandLineSyntax()
{
printf("Command line syntax:\n"
" VmaReplay <SrcFile.csv>\n");
printf(
"Command line syntax:\n"
" VmaReplay [Options] <SrcFile.csv>\n"
"Available options:\n"
" -v <Level> - Verbosity level:\n"
" 0 - Minimum verbosity. Prints only warnings and errors.\n"
" 1 - Default verbosity. Prints important messages and statistics.\n"
" 2 - Maximum verbosity. Prints a lot of information.\n"
);
}
static int ProcessFile(const char* data, size_t numBytes)
{
// Begin stats.
if(g_Verbosity > VERBOSITY::MINIMUM)
{
printf("File size: %zu B\n", numBytes);
}
LineSplit lineSplit(data, numBytes);
StrRange line;
@ -1013,28 +1062,37 @@ static int ProcessFile(const char* data, size_t numBytes)
Player player;
int result = player.Init();
if(result == 0)
{
if(g_Verbosity > VERBOSITY::MINIMUM)
{
printf("Playing...\n");
}
while(lineSplit.GetNextLine(line))
{
player.ExecuteLine(lineSplit.GetNextLineIndex(), line);
}
// End stats.
if(g_Verbosity > VERBOSITY::MINIMUM)
{
printf("Done.\n");
printf("File lines: %zu\n", lineSplit.GetNextLineIndex());
}
}
return result;
}
static int ProcessFile(const char* filePath)
static int ProcessFile()
{
printf("Loading file \"%s\"...\n", filePath);
if(g_Verbosity > VERBOSITY::MINIMUM)
{
printf("Loading file \"%s\"...\n", g_FilePath.c_str());
}
int result = 0;
FILE* file = nullptr;
const errno_t err = fopen_s(&file, filePath, "rb");
const errno_t err = fopen_s(&file, g_FilePath.c_str(), "rb");
if(err == 0)
{
_fseeki64(file, 0, SEEK_END);
@ -1066,13 +1124,64 @@ static int ProcessFile(const char* filePath)
static int main2(int argc, char** argv)
{
if(argc != 2)
CmdLineParser cmdLineParser(argc, argv);
cmdLineParser.RegisterOpt(CMD_LINE_OPT_VERBOSITY, 'v', true);
CmdLineParser::RESULT res;
while((res = cmdLineParser.ReadNext()) != CmdLineParser::RESULT_END)
{
switch(res)
{
case CmdLineParser::RESULT_OPT:
switch(cmdLineParser.GetOptId())
{
case CMD_LINE_OPT_VERBOSITY:
{
uint32_t verbosityVal = UINT32_MAX;
if(StrRangeToUint(StrRange(cmdLineParser.GetParameter()), verbosityVal) &&
verbosityVal < (uint32_t)VERBOSITY::COUNT)
{
g_Verbosity = (VERBOSITY)verbosityVal;
}
else
{
PrintCommandLineSyntax();
return RESULT_ERROR_COMMAND_LINE;
}
}
break;
default:
assert(0);
}
break;
case CmdLineParser::RESULT_PARAMETER:
if(g_FilePath.empty())
{
g_FilePath = cmdLineParser.GetParameter();
}
else
{
PrintCommandLineSyntax();
return RESULT_ERROR_COMMAND_LINE;
}
break;
case CmdLineParser::RESULT_ERROR:
PrintCommandLineSyntax();
return RESULT_ERROR_COMMAND_LINE;
break;
default:
assert(0);
}
}
if(g_FilePath.empty())
{
PrintCommandLineSyntax();
return RESULT_ERROR_COMMAND_LINE;
}
return ProcessFile(argv[1]);
return ProcessFile();
}
int main(int argc, char** argv)