CommandLine.h#

Parent directory (commandline)

Contains the CommandLine class.

Includes#

  • algorithm

  • cstdint

  • cstdlib

  • cstring

  • malloc.h

  • sstream

  • string (CompileTimeHash.h)

  • vector

Included By#

Namespaces#

Classes#

Defines#

Typedefs#

Source Code#

#pragma once
#include <vector>
#include <sstream>
#include <string>
#include <cstdint>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#if !defined(__APPLE__)
#include <malloc.h>
#endif

#if !defined(_WIN32)
#define _stricmp strcasecmp
#endif

namespace pvr {
namespace platform {

class CommandLineParser
{
public:
    class ParsedCommandLine
    {
    public:
        ParsedCommandLine() = default;
        ParsedCommandLine(int argc, char** argv)
        {
            CommandLineParser parser(argc, argv);
            *this = parser.getParsedCommandLine();
        }

        struct Option
        {
            const char* arg;
            const char* val;
            bool operator==(const Option& rhs) const { return strcmp(arg, rhs.arg) == 0; }
            bool operator==(const char* rhs) const { return strcmp(arg, rhs) == 0; }
        };
        typedef std::vector<Option> Options;

        const Options& getOptionsList() const { return _options; }

        bool hasOption(const char* name) const { return std::find(_options.begin(), _options.end(), name) != _options.end(); }

        bool getStringOption(const char* name, std::string& outValue) const
        {
            auto it = std::find(_options.begin(), _options.end(), name);
            if (it == _options.end()) { return false; }
            outValue = it->val;
            return true;
        }

        bool getStringOptionList(const char* name, std::vector<std::string>& outValues) const
        {
            std::string tmp;
            if (!getStringOption(name, tmp)) { return false; }

            if (!tmp.empty())
            {
                std::istringstream iss(tmp);
                std::string output;
                while (std::getline(iss, output, ',')) { outValues.emplace_back(output); }
            }
            return true;
        }

        bool getFloatOption(const char* name, float& outValue) const
        {
            auto it = std::find(_options.begin(), _options.end(), name);
            if (it == _options.end() || it->val == NULL) { return false; }
            outValue = static_cast<float>(atof(it->val));
            return true;
        }

        bool getIntOption(const char* name, int32_t& outValue) const
        {
            auto it = std::find(_options.begin(), _options.end(), name);
            if (it == _options.end() || it->val == NULL) { return false; }
            outValue = atoi(it->val);
            return true;
        }

        bool getBoolOptionSetTrueIfPresent(const char* name, bool& outValue) const
        {
            auto it = std::find(_options.begin(), _options.end(), name);
            if (it == _options.end()) { return false; }
            outValue = true;
            return true;
        }

        bool getBoolOptionSetFalseIfPresent(const char* name, bool& outValue) const
        {
            auto it = std::find(_options.begin(), _options.end(), name);
            if (it == _options.end()) { return false; }
            outValue = false;
            return true;
        }

    private:
        friend class CommandLineParser;
        Options _options;
    };

    CommandLineParser() : _data(0) {}

    CommandLineParser(int argc, char** argv) : _data(0) { set(argc, argv); }

    const ParsedCommandLine& getParsedCommandLine() const { return _commandLine; }

    void set(const wchar_t* cmdLine)
    {
        if (cmdLine == nullptr) { return; }

        size_t length = wcslen(cmdLine) + 1;

        std::vector<char> tmp;
        tmp.resize(length);

        while (length != 0u)
        {
            --length;
            tmp[length] = static_cast<char>(cmdLine[length]);
        }

        parseCmdLine(tmp.data());
    }

    void set(int argc, char** argv)
    {
        if (argc < 0) { return; }

        _commandLine._options.clear();

        {
            size_t length = 0;

            for (int i = 0; i < argc; ++i) { length += strlen(argv[i]) + 1; }

            _data.resize(length);
        }

        size_t offset = 0;

        for (int i = 0; i < argc; ++i)
        {
            // length
            const size_t length = strlen(argv[i]) + 1;

            memcpy(&_data[offset], argv[i], length);

            // split into var/arg
            parseArgV(&_data[offset]);

            offset += length;
        }
    }

    void set(const char* cmdLine) { parseCmdLine(cmdLine); }

    void set(const CommandLineParser& cmdLine) { *this = cmdLine; }

    void prefix(const wchar_t* cmdLine)
    {
        if (!_commandLine._options.empty() != 0u)
        {
            CommandLineParser tmp;
            tmp.set(cmdLine);
            prefix(tmp);
        }
        else
        {
            set(cmdLine);
        }
    }

    void prefix(int argc, char** argv)
    {
        if (!_commandLine._options.empty() != 0u)
        {
            CommandLineParser tmp;
            tmp.set(argc, argv);
            prefix(tmp);
        }
        else
        {
            set(argc, argv);
        }
    }

    void prefix(const char* cmdLine)
    {
        if (!_commandLine._options.empty() != 0u)
        {
            CommandLineParser tmp;
            tmp.set(cmdLine);
            prefix(tmp);
        }
        else
        {
            return set(cmdLine);
        }
    }

    void prefix(const CommandLineParser& cmdLine)
    {
        if (cmdLine._commandLine._options.empty()) { return; }

        std::vector<char> newData;
        newData.resize(_data.size() + cmdLine._data.size());

        std::vector<ParsedCommandLine::Option> newOptions;
        newOptions.resize(_commandLine._options.size() + cmdLine._commandLine._options.size());

        // copy the data
        memcpy(newData.data(), cmdLine._data.data(), cmdLine._data.size());
        memcpy(&newData[cmdLine._data.size()], _data.data(), _data.size());

        // Initialize the options
        for (uint32_t i = 0; i < cmdLine._commandLine._options.size(); ++i)
        {
            newOptions[i].arg = (const char*)((size_t)cmdLine._commandLine._options[i].arg - (size_t)cmdLine._data.data()) + (size_t)newData.data();
            newOptions[i].val = (const char*)((size_t)cmdLine._commandLine._options[i].val - (size_t)cmdLine._data.data()) + (size_t)newData.data();
        }

        for (uint32_t i = 0; i < _commandLine._options.size(); ++i)
        {
            newOptions[cmdLine._commandLine._options.size() + i].arg =
                (const char*)((size_t)_commandLine._options[i].arg - (size_t)_data.data()) + (size_t)newData.data() + cmdLine._data.size();
            newOptions[cmdLine._commandLine._options.size() + i].val =
                (const char*)((size_t)_commandLine._options[i].val - (size_t)_data.data()) + (size_t)newData.data() + cmdLine._data.size();
        }

        // Set the variables
        _data = newData;

        _commandLine._options = newOptions;
    }

    void append(const wchar_t* cmdLine)
    {
        if (!_commandLine._options.empty() != 0u)
        {
            CommandLineParser tmp;
            tmp.set(cmdLine);
            append(tmp);
        }
        else
        {
            set(cmdLine);
        }
    }

    void append(int argc, char** argv)
    {
        if (!_commandLine._options.empty() != 0u)
        {
            CommandLineParser tmp;
            tmp.set(argc, argv);
            append(tmp);
        }
        else
        {
            set(argc, argv);
        }
    }

    void append(const char* cmdLine)
    {
        if (!_commandLine._options.empty() != 0u)
        {
            CommandLineParser tmp;
            tmp.set(cmdLine);
            append(tmp);
        }
        else
        {
            set(cmdLine);
        }
    }

    void append(const CommandLineParser& cmdLine)
    {
        if (cmdLine._commandLine._options.empty()) { return; }

        std::vector<char> newData;
        newData.resize(_data.size() + cmdLine._data.size());

        std::vector<ParsedCommandLine::Option> newOptions;
        newOptions.resize(_commandLine._options.size() + cmdLine._commandLine._options.size());

        // copy the data
        memcpy(newData.data(), _data.data(), _data.size());
        memcpy(&newData[_data.size()], cmdLine._data.data(), cmdLine._data.size());

        // Initialize the options
        for (uint32_t i = 0; i < _commandLine._options.size(); ++i)
        {
            newOptions[i].arg = (const char*)((size_t)_commandLine._options[i].arg - (size_t)_data.data()) + (size_t)newData.data();
            newOptions[i].val = (const char*)((size_t)_commandLine._options[i].val - (size_t)_data.data()) + (size_t)newData.data();
        }

        for (uint32_t i = 0; i < cmdLine._commandLine._options.size(); ++i)
        {
            newOptions[_commandLine._options.size() + i].arg =
                (const char*)((size_t)cmdLine._commandLine._options[i].arg - (size_t)cmdLine._data.data()) + (size_t)newData.data() + _data.size();
            newOptions[_commandLine._options.size() + i].val =
                (const char*)((size_t)cmdLine._commandLine._options[i].val - (size_t)cmdLine._data.data()) + (size_t)newData.data() + _data.size();
        }

        // Set the variables
        _data = newData;
        _commandLine._options = newOptions;
    }

protected:
    void parseCmdLine(const char* const cmdLine)
    {
        size_t len;
        uint32_t nIn, nOut;
        bool bInQuotes;
        ParsedCommandLine::Option opt;

        if (cmdLine == nullptr) { return; }

        // Take a copy of the original
        len = strlen(cmdLine) + 1;

        // Take a copy to be edited
        _data.resize(len);

        // Break the command line into options
        bInQuotes = false;
        opt.arg = nullptr;
        opt.val = nullptr;
        nIn = static_cast<uint32_t>(-1);
        nOut = 0;
        do {
            ++nIn;
            if (cmdLine[nIn] == '"') { bInQuotes = !bInQuotes; }
            else
            {
                if (bInQuotes && cmdLine[nIn] != 0)
                {
                    if (opt.arg == nullptr) { opt.arg = &_data[nOut]; }

                    _data[nOut++] = cmdLine[nIn];
                }
                else
                {
                    switch (cmdLine[nIn])
                    {
                    case '=':
                        _data[nOut++] = 0;
                        opt.val = &_data[nOut];
                        break;

                    case ' ':
                    case '\t':
                    case '\0':
                        _data[nOut++] = 0;
                        if ((opt.arg != nullptr) || (opt.val != nullptr))
                        {
                            // Add option to list
                            _commandLine._options.emplace_back(opt);

                            opt.arg = nullptr;
                            opt.val = nullptr;
                        }
                        break;

                    default:
                        if (opt.arg == nullptr) { opt.arg = &_data[nOut]; }

                        _data[nOut++] = cmdLine[nIn];
                        break;
                    }
                }
            }
        } while (cmdLine[nIn] != 0);
    }

    void parseArgV(char* arg)
    {
        ParsedCommandLine::Option opt;
        size_t j;

        // Hunt for an = symbol
        for (j = 0; (arg[j] != 0) && arg[j] != '='; ++j) { ; }

        opt.arg = arg;
        if (arg[j] != 0)
        {
            // terminate the arg std::string, set value std::string
            arg[j] = 0;
            opt.val = &arg[j + 1];
        }
        else
        {
            // no value specified
            opt.val = nullptr;
        }

        // Add option to list
        _commandLine._options.emplace_back(opt);
    }

private:
    uint32_t findArg(const char* arg) const
    {
        uint32_t i;

        /*
            Find an argument, case insensitive. Returns the index of the option
            if found, or the number of options if not.
        */
        for (i = 0; i < _commandLine._options.size(); ++i)
        {
            if (_stricmp(_commandLine._options[i].arg, arg) == 0) { break; }
        }

        return i;
    }
    bool readFlag(const char* arg, bool& bVal) const
    {
        uint32_t nIdx = findArg(arg);

        if (nIdx == _commandLine._options.size()) { return false; }
        // a flag must have no value
        bVal = _commandLine._options[nIdx].val != nullptr ? false : true;
        return true;
    }
    bool readUint(const char* arg, uint32_t& val) const
    {
        uint32_t nIdx = findArg(arg);

        if (nIdx == _commandLine._options.size()) { return false; }
        val = static_cast<uint32_t>(atoi(_commandLine._options[nIdx].val));
        return true;
    }
    bool readFloat(const char* arg, float& val) const
    {
        uint32_t nIdx = findArg(arg);

        if (nIdx == _commandLine._options.size()) { return false; }
        val = static_cast<float>(atof(_commandLine._options[nIdx].val));
        return true;
    }
    std::vector<char> _data;
    ParsedCommandLine _commandLine;
};
} // namespace platform
typedef platform::CommandLineParser::ParsedCommandLine CommandLine;
} // namespace pvr