mirror of https://github.com/infosecn1nja/C3.git
Add first channel creation
parent
eee77a3cd6
commit
7747a637a8
|
@ -1,5 +1,5 @@
|
||||||
#include "StdAfx.h"
|
#include "StdAfx.h"
|
||||||
#include "InputContext.h"
|
#include "Core/Profiler.h"
|
||||||
|
|
||||||
namespace MWR::C3::Linter
|
namespace MWR::C3::Linter
|
||||||
{
|
{
|
||||||
|
@ -15,8 +15,35 @@ namespace MWR::C3::Linter
|
||||||
{
|
{
|
||||||
return InterfaceFactory::Instance().Find<AbstractChannel>(channelName)->second;
|
return InterfaceFactory::Instance().Find<AbstractChannel>(channelName)->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// template to avoid typing the whole name
|
||||||
|
template<class T>
|
||||||
|
auto GetChannelCapability(T const& channelInfo)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return json::parse(channelInfo.m_Capability);
|
||||||
|
}
|
||||||
|
catch (json::parse_error& e)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to parse channel's capability json. "s + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// template to avoid typing the whole name
|
||||||
|
template<class T>
|
||||||
|
auto MakeDevice(MWR::json const& createParams, const T& chInfo)
|
||||||
|
{
|
||||||
|
auto blob = MWR::C3::Core::Profiler::TranslateArguments(createParams);
|
||||||
|
return chInfo.m_Builder(blob);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
#define DebugDump(x) MWR::C3::Linter::OutputDebug(x)
|
||||||
|
#else
|
||||||
|
#define DebugDump(x)
|
||||||
|
#endif // _DEBUG
|
||||||
|
|
||||||
/// Entry point of the application.
|
/// Entry point of the application.
|
||||||
/// @param argc number of program arguments.
|
/// @param argc number of program arguments.
|
||||||
|
@ -25,27 +52,25 @@ int main(DWORD argc, char* argv[])
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using namespace MWR;
|
using namespace MWR;
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
std::cout << "Custom Command and Control - Channel linter. BUILD: " << C3_BUILD_VERSION << std::endl;
|
std::cout << "Custom Command and Control - Channel linter. BUILD: " << C3_BUILD_VERSION << std::endl;
|
||||||
C3::Linter::InputContext context(argc, argv);
|
C3::Linter::InputContext context(argc, argv);
|
||||||
|
auto const& config = context.GetConfig();
|
||||||
|
|
||||||
// select channel
|
// select channel
|
||||||
auto const& chInfo = C3::Linter::GetChannelInfo(context.GetChannelName());
|
auto const& chInfo = C3::Linter::GetChannelInfo(config.m_ChannelName);
|
||||||
|
|
||||||
// read create and prompt for arguments
|
// read create and prompt for arguments
|
||||||
auto capability = json::parse(chInfo.m_Capability);
|
auto capability = C3::Linter::GetChannelCapability(chInfo);
|
||||||
C3::Linter::OutputDebug(capability.dump(4));
|
|
||||||
return 0;
|
std::cout << "Create channel 1" << std::endl;
|
||||||
}
|
C3::Linter::Form form(capability.at("/create/arguments"_json_pointer));
|
||||||
catch (MWR::C3::Linter::InputError & e)
|
auto createParams = form.FillForm(config.m_ChannelArguments);
|
||||||
{
|
auto channel = C3::Linter::MakeDevice(createParams, chInfo);
|
||||||
std::cerr << e.what() << std::endl;
|
|
||||||
std::cerr << MWR::C3::Linter::InputContext::GetUsage() << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
catch (std::exception & e)
|
catch (std::exception & e)
|
||||||
{
|
{
|
||||||
std::cerr << "Error occurred: " << e.what() << std::endl;
|
std::cerr << e.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,8 @@
|
||||||
<ProjectGuid>{F2EC73D1-D533-4EE4-955A-A62E306472CC}</ProjectGuid>
|
<ProjectGuid>{F2EC73D1-D533-4EE4-955A-A62E306472CC}</ProjectGuid>
|
||||||
<Keyword>Win32Proj</Keyword>
|
<Keyword>Win32Proj</Keyword>
|
||||||
<RootNamespace>CableTester</RootNamespace>
|
<RootNamespace>CableTester</RootNamespace>
|
||||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
|
||||||
<ProjectName>ChannelLinter</ProjectName>
|
<ProjectName>ChannelLinter</ProjectName>
|
||||||
|
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
@ -296,12 +296,16 @@
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClInclude Include="Form.h" />
|
||||||
<ClInclude Include="InputContext.h" />
|
<ClInclude Include="InputContext.h" />
|
||||||
|
<ClInclude Include="InputVector.h" />
|
||||||
<ClInclude Include="StdAfx.h" />
|
<ClInclude Include="StdAfx.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="ChannelLinter.cpp" />
|
<ClCompile Include="ChannelLinter.cpp" />
|
||||||
|
<ClCompile Include="Form.cpp" />
|
||||||
<ClCompile Include="InputContext.cpp" />
|
<ClCompile Include="InputContext.cpp" />
|
||||||
|
<ClCompile Include="InputVector.cpp" />
|
||||||
<ClCompile Include="StdAfx.cpp">
|
<ClCompile Include="StdAfx.cpp">
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "Form.h"
|
||||||
|
|
||||||
|
namespace MWR::C3::Linter
|
||||||
|
{
|
||||||
|
|
||||||
|
Form::Form(json argumentForm) : m_ArgumentsForm(std::move(argumentForm))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
json Form::FillForm(InputVector input)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto createParams = m_ArgumentsForm;
|
||||||
|
auto fillArg = [&input](json& arg)
|
||||||
|
{
|
||||||
|
auto& name = arg.at("name").get_ref<std::string const&>();
|
||||||
|
auto& type = arg.at("type").get_ref<std::string const&>();
|
||||||
|
arg["value"] = input.GetNext();
|
||||||
|
// TODO input validation against m_ArgumentsForm min, max, type, etc.
|
||||||
|
};
|
||||||
|
for (size_t i = 0; i < createParams.size(); ++i)
|
||||||
|
{
|
||||||
|
auto& arg = createParams[i];
|
||||||
|
if (arg.is_array())
|
||||||
|
for (size_t j = 0; j < arg.size(); ++j)
|
||||||
|
fillArg(arg[j]);
|
||||||
|
else
|
||||||
|
fillArg(arg);
|
||||||
|
}
|
||||||
|
return createParams;
|
||||||
|
}
|
||||||
|
catch (std::out_of_range&)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to create channel: not enough arguments given");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace MWR::C3::Linter
|
||||||
|
{
|
||||||
|
class Form
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Form(json argumentForm);
|
||||||
|
|
||||||
|
json FillForm(InputVector input);
|
||||||
|
|
||||||
|
private:
|
||||||
|
json m_ArgumentsForm;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -3,21 +3,33 @@
|
||||||
|
|
||||||
namespace MWR::C3::Linter
|
namespace MWR::C3::Linter
|
||||||
{
|
{
|
||||||
InputContext::InputContext(int argc, char* argv[])
|
namespace
|
||||||
{
|
{
|
||||||
if (argc < 2)
|
InputContext::Config CreateConfig(argparse::ArgumentParser const& parser)
|
||||||
throw InputError("No channel name specified");
|
{
|
||||||
|
InputContext::Config config;
|
||||||
m_ChannelName = argv[1];
|
config.m_ChannelName = parser.retrieve<std::string>("name");
|
||||||
|
config.m_ChannelArguments = parser.retrieve<std::vector<std::string>>("args");
|
||||||
|
return config;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view InputContext::GetChannelName() const
|
InputContext::InputContext(int argc, char** argv) : m_ArgParser()
|
||||||
{
|
{
|
||||||
return m_ChannelName;
|
AddOptions();
|
||||||
|
m_ArgParser.parse(argc, argv);
|
||||||
|
m_Config = CreateConfig(m_ArgParser);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view InputContext::GetUsage()
|
void InputContext::AddOptions()
|
||||||
{
|
{
|
||||||
return "TODO";
|
m_ArgParser.addArgument("-n", "--name", 1, false);
|
||||||
|
m_ArgParser.addArgument("-a", "--args", '*', false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string InputContext::GetUsage()
|
||||||
|
{
|
||||||
|
return m_ArgParser.usage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,29 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "argparse.hpp"
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace MWR::C3::Linter
|
namespace MWR::C3::Linter
|
||||||
{
|
{
|
||||||
struct InputError : public std::exception
|
using StringVector = std::vector<std::string>;
|
||||||
{
|
|
||||||
using std::exception::exception;
|
|
||||||
using std::exception::what;
|
|
||||||
};
|
|
||||||
|
|
||||||
class InputContext {
|
class InputContext {
|
||||||
public:
|
public:
|
||||||
InputContext(int argc, char* argv[]);
|
InputContext(int argc, char** argv);
|
||||||
|
|
||||||
std::string_view GetChannelName() const;
|
std::string GetUsage();
|
||||||
|
|
||||||
static std::string_view GetUsage();
|
struct Config {
|
||||||
|
std::string m_ChannelName;
|
||||||
|
StringVector m_ChannelArguments;
|
||||||
|
std::optional<StringVector> m_ComplementaryChannelArguments;
|
||||||
|
};
|
||||||
|
|
||||||
|
Config const& GetConfig() const { return m_Config; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_ChannelName;
|
void AddOptions();
|
||||||
|
argparse::ArgumentParser m_ArgParser;
|
||||||
|
Config m_Config;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
namespace MWR::C3::Linter
|
||||||
|
{
|
||||||
|
InputVector::InputVector(StringVector input) : m_Input{ std::move(input) }, m_Current{ m_Input.cbegin() }
|
||||||
|
{
|
||||||
|
m_Current = m_Input.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputVector::InputVector(InputVector const& other) :
|
||||||
|
m_Input{ other.m_Input },
|
||||||
|
m_Current{ m_Input.cbegin() + std::distance(other.m_Input.begin(), other.m_Current) }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InputVector& InputVector::operator=(InputVector const& other)
|
||||||
|
{
|
||||||
|
InputVector tmp(other);
|
||||||
|
std::swap(tmp, *this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string InputVector::GetNext()
|
||||||
|
{
|
||||||
|
if (m_Current == m_Input.cend())
|
||||||
|
throw std::out_of_range("Input vector has no more elements");
|
||||||
|
return *m_Current++;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace MWR::C3::Linter
|
||||||
|
{
|
||||||
|
class InputVector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InputVector(StringVector input);
|
||||||
|
InputVector(InputVector const& other);
|
||||||
|
InputVector& operator = (InputVector const& other);
|
||||||
|
|
||||||
|
std::string GetNext();
|
||||||
|
private:
|
||||||
|
StringVector m_Input;
|
||||||
|
StringVector::const_iterator m_Current;
|
||||||
|
};
|
||||||
|
}
|
|
@ -5,7 +5,14 @@
|
||||||
|
|
||||||
// Standard library includes.
|
// Standard library includes.
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
// C3 inclusion.
|
// C3 inclusion.
|
||||||
#include "Common/MWR/C3/Sdk.hpp"
|
#include "Common/MWR/C3/Sdk.hpp"
|
||||||
#include "Common/C3_BUILD_VERSION_HASH_PART.hxx"
|
#include "Common/C3_BUILD_VERSION_HASH_PART.hxx"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
#include "InputContext.h"
|
||||||
|
#include "InputVector.h"
|
||||||
|
#include "Form.h"
|
||||||
|
|
|
@ -0,0 +1,455 @@
|
||||||
|
#ifndef ARGPARSE_HPP_
|
||||||
|
#define ARGPARSE_HPP_
|
||||||
|
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
#include <unordered_map>
|
||||||
|
typedef std::unordered_map<std::string, size_t> IndexMap;
|
||||||
|
#else
|
||||||
|
#include <map>
|
||||||
|
typedef std::map<std::string, size_t> IndexMap;
|
||||||
|
#endif
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace argparse {
|
||||||
|
|
||||||
|
/*! @class ArgumentParser
|
||||||
|
* @brief A simple command-line argument parser based on the design of
|
||||||
|
* python's parser of the same name.
|
||||||
|
*
|
||||||
|
* ArgumentParser is a simple C++ class that can parse arguments from
|
||||||
|
* the command-line or any array of strings. The syntax is familiar to
|
||||||
|
* anyone who has used python's ArgumentParser:
|
||||||
|
* \code
|
||||||
|
* // create a parser and add the options
|
||||||
|
* ArgumentParser parser;
|
||||||
|
* parser.addArgument("-n", "--name");
|
||||||
|
* parser.addArgument("--inputs", '+');
|
||||||
|
*
|
||||||
|
* // parse the command-line arguments
|
||||||
|
* parser.parse(argc, argv);
|
||||||
|
*
|
||||||
|
* // get the inputs and iterate over them
|
||||||
|
* string name = parser.retrieve("name");
|
||||||
|
* vector<string> inputs = parser.retrieve<vector<string>>("inputs");
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ArgumentParser {
|
||||||
|
private:
|
||||||
|
class Any;
|
||||||
|
struct Argument;
|
||||||
|
class PlaceHolder;
|
||||||
|
class Holder;
|
||||||
|
typedef std::string String;
|
||||||
|
typedef std::vector<Any> AnyVector;
|
||||||
|
typedef std::vector<String> StringVector;
|
||||||
|
typedef std::vector<Argument> ArgumentVector;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// Type-erasure internal storage
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
class Any {
|
||||||
|
public:
|
||||||
|
// constructor
|
||||||
|
Any() : content(0) {}
|
||||||
|
// destructor
|
||||||
|
~Any() { delete content; }
|
||||||
|
// INWARD CONVERSIONS
|
||||||
|
Any(const Any& other) : content(other.content ? other.content->clone() : 0) {}
|
||||||
|
template <typename ValueType>
|
||||||
|
Any(const ValueType& other)
|
||||||
|
: content(new Holder<ValueType>(other)) {}
|
||||||
|
Any& swap(Any& other) {
|
||||||
|
std::swap(content, other.content);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
Any& operator=(const Any& rhs) {
|
||||||
|
Any tmp(rhs);
|
||||||
|
return swap(tmp);
|
||||||
|
}
|
||||||
|
template <typename ValueType>
|
||||||
|
Any& operator=(const ValueType& rhs) {
|
||||||
|
Any tmp(rhs);
|
||||||
|
return swap(tmp);
|
||||||
|
}
|
||||||
|
// OUTWARD CONVERSIONS
|
||||||
|
template <typename ValueType>
|
||||||
|
ValueType* toPtr() const {
|
||||||
|
return content->type_info() == typeid(ValueType)
|
||||||
|
? &static_cast<Holder<ValueType>*>(content)->held_
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
template <typename ValueType>
|
||||||
|
ValueType& castTo() {
|
||||||
|
if (!toPtr<ValueType>()) throw std::bad_cast();
|
||||||
|
return *toPtr<ValueType>();
|
||||||
|
}
|
||||||
|
template <typename ValueType>
|
||||||
|
const ValueType& castTo() const {
|
||||||
|
if (!toPtr<ValueType>()) throw std::bad_cast();
|
||||||
|
return *toPtr<ValueType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Inner placeholder interface
|
||||||
|
class PlaceHolder {
|
||||||
|
public:
|
||||||
|
virtual ~PlaceHolder() {}
|
||||||
|
virtual const std::type_info& type_info() const = 0;
|
||||||
|
virtual PlaceHolder* clone() const = 0;
|
||||||
|
};
|
||||||
|
// Inner template concrete instantiation of PlaceHolder
|
||||||
|
template <typename ValueType>
|
||||||
|
class Holder : public PlaceHolder {
|
||||||
|
public:
|
||||||
|
ValueType held_;
|
||||||
|
Holder(const ValueType& value) : held_(value) {}
|
||||||
|
virtual const std::type_info& type_info() const { return typeid(ValueType); }
|
||||||
|
virtual PlaceHolder* clone() const { return new Holder(held_); }
|
||||||
|
};
|
||||||
|
PlaceHolder* content;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// Argument
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
static String delimit(const String& name) {
|
||||||
|
return String(std::min(name.size(), (size_t)2), '-').append(name);
|
||||||
|
}
|
||||||
|
static String strip(const String& name) {
|
||||||
|
size_t begin = 0;
|
||||||
|
begin += name.size() > 0 ? name[0] == '-' : 0;
|
||||||
|
begin += name.size() > 3 ? name[1] == '-' : 0;
|
||||||
|
return name.substr(begin);
|
||||||
|
}
|
||||||
|
static String upper(const String& in) {
|
||||||
|
String out(in);
|
||||||
|
std::transform(out.begin(), out.end(), out.begin(), ::toupper);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
static String escape(const String& in) {
|
||||||
|
String out(in);
|
||||||
|
if (in.find(' ') != std::string::npos) out = String("\"").append(out).append("\"");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Argument {
|
||||||
|
Argument() : short_name(""), name(""), optional(true), used(false), fixed_nargs(0), fixed(true) {}
|
||||||
|
Argument(const String& _short_name, const String& _name, bool _optional, char nargs)
|
||||||
|
: short_name(_short_name), name(_name), optional(_optional), used(false) {
|
||||||
|
if (nargs == '+' || nargs == '*') {
|
||||||
|
variable_nargs = nargs;
|
||||||
|
fixed = false;
|
||||||
|
} else {
|
||||||
|
fixed_nargs = nargs;
|
||||||
|
fixed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String short_name;
|
||||||
|
String name;
|
||||||
|
bool optional;
|
||||||
|
bool used = false;
|
||||||
|
union {
|
||||||
|
size_t fixed_nargs;
|
||||||
|
char variable_nargs;
|
||||||
|
};
|
||||||
|
bool fixed;
|
||||||
|
String canonicalName() const { return (name.empty()) ? short_name : name; }
|
||||||
|
String toString(bool named = true) const {
|
||||||
|
std::ostringstream s;
|
||||||
|
String uname = name.empty() ? upper(strip(short_name)) : upper(strip(name));
|
||||||
|
if (named && optional) s << "[";
|
||||||
|
if (named) s << canonicalName();
|
||||||
|
if (fixed) {
|
||||||
|
size_t N = std::min((size_t)3, fixed_nargs);
|
||||||
|
for (size_t n = 0; n < N; ++n) s << " " << uname;
|
||||||
|
if (N < fixed_nargs) s << " ...";
|
||||||
|
}
|
||||||
|
if (!fixed) {
|
||||||
|
s << " ";
|
||||||
|
if (variable_nargs == '*') s << "[";
|
||||||
|
s << uname << " ";
|
||||||
|
if (variable_nargs == '+') s << "[";
|
||||||
|
s << uname << "...]";
|
||||||
|
}
|
||||||
|
if (named && optional) s << "]";
|
||||||
|
return s.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void insertArgument(const Argument& arg) {
|
||||||
|
size_t N = arguments_.size();
|
||||||
|
arguments_.push_back(arg);
|
||||||
|
if (arg.fixed && arg.fixed_nargs <= 1) {
|
||||||
|
variables_.push_back(String());
|
||||||
|
} else {
|
||||||
|
variables_.push_back(StringVector());
|
||||||
|
}
|
||||||
|
if (!arg.short_name.empty()) index_[arg.short_name] = N;
|
||||||
|
if (!arg.name.empty()) index_[arg.name] = N;
|
||||||
|
if (!arg.optional) required_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// Error handling
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
void argumentError(const std::string& msg, bool show_usage = false) {
|
||||||
|
if (use_exceptions_) throw std::invalid_argument(msg);
|
||||||
|
std::cerr << "ArgumentParser error: " << msg << std::endl;
|
||||||
|
if (show_usage) std::cerr << usage() << std::endl;
|
||||||
|
exit(-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// Member variables
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
IndexMap index_;
|
||||||
|
bool ignore_first_;
|
||||||
|
bool use_exceptions_;
|
||||||
|
size_t required_;
|
||||||
|
String app_name_;
|
||||||
|
String final_name_;
|
||||||
|
ArgumentVector arguments_;
|
||||||
|
AnyVector variables_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ArgumentParser() : ignore_first_(true), use_exceptions_(false), required_(0) {}
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// addArgument
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
void appName(const String& name) { app_name_ = name; }
|
||||||
|
void addArgument(const String& name, char nargs = 0, bool optional = true) {
|
||||||
|
if (name.size() > 2) {
|
||||||
|
Argument arg("", verify(name), optional, nargs);
|
||||||
|
insertArgument(arg);
|
||||||
|
} else {
|
||||||
|
Argument arg(verify(name), "", optional, nargs);
|
||||||
|
insertArgument(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void addArgument(const String& short_name, const String& name, char nargs = 0,
|
||||||
|
bool optional = true) {
|
||||||
|
Argument arg(verify(short_name), verify(name), optional, nargs);
|
||||||
|
insertArgument(arg);
|
||||||
|
}
|
||||||
|
void addFinalArgument(const String& name, char nargs = 1, bool optional = false) {
|
||||||
|
final_name_ = delimit(name);
|
||||||
|
Argument arg("", final_name_, optional, nargs);
|
||||||
|
insertArgument(arg);
|
||||||
|
}
|
||||||
|
void ignoreFirstArgument(bool ignore_first) { ignore_first_ = ignore_first; }
|
||||||
|
String verify(const String& name) {
|
||||||
|
if (name.empty()) argumentError("argument names must be non-empty");
|
||||||
|
if ((name.size() == 2 && name[0] != '-') || name.size() == 3)
|
||||||
|
argumentError(String("invalid argument '")
|
||||||
|
.append(name)
|
||||||
|
.append("'. Short names must begin with '-'"));
|
||||||
|
if (name.size() > 3 && (name[0] != '-' || name[1] != '-'))
|
||||||
|
argumentError(String("invalid argument '")
|
||||||
|
.append(name)
|
||||||
|
.append("'. Multi-character names must begin with '--'"));
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// Parse
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
void parse(size_t argc, char** argv) { parse(StringVector(argv, argv + argc)); }
|
||||||
|
|
||||||
|
void parse(const StringVector& argv) {
|
||||||
|
// check if the app is named
|
||||||
|
if (app_name_.empty() && ignore_first_ && !argv.empty()) app_name_ = argv[0];
|
||||||
|
|
||||||
|
// set up the working set
|
||||||
|
Argument active;
|
||||||
|
Argument final = final_name_.empty() ? Argument() : arguments_[index_[final_name_]];
|
||||||
|
size_t consumed = 0;
|
||||||
|
size_t nrequired = final.optional ? required_ : required_ - 1;
|
||||||
|
size_t nfinal = final.optional ? 0 : (final.fixed ? final.fixed_nargs
|
||||||
|
: (final.variable_nargs == '+' ? 1 : 0));
|
||||||
|
|
||||||
|
// iterate over each element of the array
|
||||||
|
for (StringVector::const_iterator in = argv.begin() + ignore_first_;
|
||||||
|
in < argv.end() - nfinal; ++in) {
|
||||||
|
String active_name = active.canonicalName();
|
||||||
|
String el = *in;
|
||||||
|
// check if the element is a key
|
||||||
|
if (index_.count(el) == 0) {
|
||||||
|
// input
|
||||||
|
// is the current active argument expecting more inputs?
|
||||||
|
if (active.fixed && active.fixed_nargs <= consumed)
|
||||||
|
argumentError(String("attempt to pass too many inputs to ").append(active_name),
|
||||||
|
true);
|
||||||
|
if (active.fixed && active.fixed_nargs == 1) {
|
||||||
|
variables_[index_[active_name]].castTo<String>() = el;
|
||||||
|
} else {
|
||||||
|
variables_[index_[active_name]].castTo<StringVector>().push_back(el);
|
||||||
|
}
|
||||||
|
consumed++;
|
||||||
|
} else {
|
||||||
|
// new key!
|
||||||
|
// has the active argument consumed enough elements?
|
||||||
|
if ((active.fixed && active.fixed_nargs != consumed) ||
|
||||||
|
(!active.fixed && active.variable_nargs == '+' && consumed < 1))
|
||||||
|
argumentError(String("encountered argument ")
|
||||||
|
.append(el)
|
||||||
|
.append(" when expecting more inputs to ")
|
||||||
|
.append(active_name),
|
||||||
|
true);
|
||||||
|
active = arguments_[index_[el]];
|
||||||
|
arguments_[index_[el]].used = true;
|
||||||
|
// check if we've satisfied the required arguments
|
||||||
|
if (active.optional && nrequired > 0)
|
||||||
|
argumentError(String("encountered optional argument ")
|
||||||
|
.append(el)
|
||||||
|
.append(" when expecting more required arguments"),
|
||||||
|
true);
|
||||||
|
// are there enough arguments for the new argument to consume?
|
||||||
|
if ((active.fixed && active.fixed_nargs > (argv.end() - in - nfinal - 1)) ||
|
||||||
|
(!active.fixed && active.variable_nargs == '+' &&
|
||||||
|
!(argv.end() - in - nfinal - 1)))
|
||||||
|
argumentError(String("too few inputs passed to argument ").append(el), true);
|
||||||
|
if (!active.optional) nrequired--;
|
||||||
|
consumed = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (StringVector::const_iterator in =
|
||||||
|
std::max(argv.begin() + ignore_first_, argv.end() - nfinal);
|
||||||
|
in != argv.end(); ++in) {
|
||||||
|
String el = *in;
|
||||||
|
// check if we accidentally find an argument specifier
|
||||||
|
if (index_.count(el))
|
||||||
|
argumentError(String("encountered argument specifier ")
|
||||||
|
.append(el)
|
||||||
|
.append(" while parsing final required inputs"),
|
||||||
|
true);
|
||||||
|
if (final.fixed && final.fixed_nargs == 1) {
|
||||||
|
variables_[index_[final_name_]].castTo<String>() = el;
|
||||||
|
} else {
|
||||||
|
variables_[index_[final_name_]].castTo<StringVector>().push_back(el);
|
||||||
|
}
|
||||||
|
nfinal--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that all of the required arguments have been encountered
|
||||||
|
if (nrequired > 0 || nfinal > 0)
|
||||||
|
argumentError(String("too few required arguments passed to ").append(app_name_), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// Retrieve
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
template <typename T>
|
||||||
|
const T& retrieve(const String& name) const {
|
||||||
|
if (index_.count(delimit(name)) == 0) throw std::out_of_range("Key not found");
|
||||||
|
size_t N = index_.at(delimit(name));
|
||||||
|
return variables_[N].castTo<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// Retrieve
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
template <typename T>
|
||||||
|
T& retrieve(const String& name) {
|
||||||
|
if (index_.count(delimit(name)) == 0) throw std::out_of_range("Key not found");
|
||||||
|
size_t N = index_.at(delimit(name));
|
||||||
|
return variables_[N].castTo<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// Properties
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
String usage() {
|
||||||
|
// premable app name
|
||||||
|
std::ostringstream help;
|
||||||
|
help << "Usage: " << escape(app_name_);
|
||||||
|
size_t indent = help.str().size();
|
||||||
|
size_t linelength = 0;
|
||||||
|
|
||||||
|
// get the required arguments
|
||||||
|
for (ArgumentVector::const_iterator it = arguments_.begin(); it != arguments_.end(); ++it) {
|
||||||
|
Argument arg = *it;
|
||||||
|
if (arg.optional) continue;
|
||||||
|
if (arg.name.compare(final_name_) == 0) continue;
|
||||||
|
help << " ";
|
||||||
|
String argstr = arg.toString();
|
||||||
|
if (argstr.size() + linelength > 80) {
|
||||||
|
help << "\n" << String(indent, ' ');
|
||||||
|
linelength = 0;
|
||||||
|
} else {
|
||||||
|
linelength += argstr.size();
|
||||||
|
}
|
||||||
|
help << argstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the optional arguments
|
||||||
|
for (ArgumentVector::const_iterator it = arguments_.begin(); it != arguments_.end(); ++it) {
|
||||||
|
Argument arg = *it;
|
||||||
|
if (!arg.optional) continue;
|
||||||
|
if (arg.name.compare(final_name_) == 0) continue;
|
||||||
|
help << " ";
|
||||||
|
String argstr = arg.toString();
|
||||||
|
if (argstr.size() + linelength > 80) {
|
||||||
|
help << "\n" << String(indent, ' ');
|
||||||
|
linelength = 0;
|
||||||
|
} else {
|
||||||
|
linelength += argstr.size();
|
||||||
|
}
|
||||||
|
help << argstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the final argument
|
||||||
|
if (!final_name_.empty()) {
|
||||||
|
Argument arg = arguments_[index_[final_name_]];
|
||||||
|
String argstr = arg.toString(false);
|
||||||
|
if (argstr.size() + linelength > 80) {
|
||||||
|
help << "\n" << String(indent, ' ');
|
||||||
|
linelength = 0;
|
||||||
|
} else {
|
||||||
|
linelength += argstr.size();
|
||||||
|
}
|
||||||
|
help << argstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return help.str();
|
||||||
|
}
|
||||||
|
void useExceptions(bool state) { use_exceptions_ = state; }
|
||||||
|
bool empty() const { return index_.empty(); }
|
||||||
|
void clear() {
|
||||||
|
ignore_first_ = true;
|
||||||
|
required_ = 0;
|
||||||
|
index_.clear();
|
||||||
|
arguments_.clear();
|
||||||
|
variables_.clear();
|
||||||
|
}
|
||||||
|
bool exists(const String& name) const {
|
||||||
|
auto it = index_.find(delimit(name));
|
||||||
|
return (it != index_.end()) && (arguments_[it->second].used);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t count(const String& name) const {
|
||||||
|
// check if the name is an argument
|
||||||
|
if (index_.count(delimit(name)) == 0) return 0;
|
||||||
|
size_t N = index_.at(delimit(name));
|
||||||
|
Argument arg = arguments_[N];
|
||||||
|
Any var = variables_[N];
|
||||||
|
// check if the argument is a vector
|
||||||
|
if (arg.fixed) {
|
||||||
|
return !var.castTo<String>().empty();
|
||||||
|
} else {
|
||||||
|
return var.castTo<StringVector>().size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace argparse
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue