/*========================== begin_copyright_notice ============================

Copyright (C) 2017-2021 Intel Corporation

SPDX-License-Identifier: MIT

============================= end_copyright_notice ===========================*/

#include "Option.h"
#include "Timer.h"
#include "G4_Opcode.h"          // temporarily add to support G4_MAX_SRCS == 3
#include "DebugInfo.h"
#include "PlatformInfo.h"
#include "IGC/common/StringMacros.hpp"

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
#include <sstream>

#ifdef _MSC_VER
// MAX : Disable security enhancements warning (VC2005)
// 4312: disable cast warning on 64bit build (option handling's macro need to be re-done)
#pragma warning (disable: 4996 4312)
#endif

// NOTE: for now when adding a new flag, please also update ::reset() so that it is
// reset to the default value after the builder is destroyed.  We will move these options to be
// part of the builder in the near future.

static std::string makePlatformsString()
{
    int n = 0;
    auto platforms = vISA::PlatformInfo::getGenxAllPlatforms(&n);
    std::stringstream ss;
    for (int i = 0; i < n; i++) {
        if (i > 0) {
            ss << ", ";
        }
        const char * name = vISA::PlatformInfo::getGenxPlatformString(platforms[i]);
        assert(name);
        ss << name;
    }
    return ss.str();
}

/*
    All arguments should be valid BE options.
    When invoked from main the 0 argument which is a name of the program
    should be skipped when passed in.

    If the first argument passed in is *.isaasm or -CISAbinary it
    will go through parser mode in which it will parse *.isaasm files and create either regular or fat .isa file.
    User can either pass in individual *.isaasm file, or multiple files in a text file using -CISAbinary option.
    Either one or the other method is allowed, but not both on the same command line.

    If first name that is passed in ends with *.isa it treats it as an offline compilation of .isa file.
*/
bool Options::parseOptions(int argc, const char* argv[])
{
    int startPos = 0;
    assert(!argToOption.empty() && "Must be initialized first!");

#define MAX_ARGC 128
    MUST_BE_TRUE(argc < MAX_ARGC, "too many options for vISA builder");

    for (int i = 0; i < argc; ++i)
    {
        argString << argv[i] << " ";
    }

    for (int i = startPos; i < argc; i++)
    {
        if (argv[i] == nullptr)
        {
            std::cerr << "INTERNAL ERROR: nullptr argv element\n";
            return false;
        }
        // If arg not defined in the .def file, exit with an error
        auto it = argToOption.find(argv[i]);
        if (it == argToOption.end()) {
            std::cerr << argv[i] << ": unrecognized option\n";
            showUsage(std::cout);
            return false;
        }
        bool parseError = false;
        auto parseU64 = [&] (uint64_t &val) -> bool {
            char *end = nullptr;
            const char *arg = argv[i];
            uint64_t x = 0;
            if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X')) {
                x = std::strtoull(arg + 2, &end, 16);
            } else {
                x = std::strtoull(arg, &end, 10);
            }
            if (*end != 0) {
                parseError = true;
                std::cerr << it->first << ": " << argv[i] << ": malformed integer\n";
                return false;
            }
            val = x;
            return true;
        };
        auto parseU32 = [&] (uint32_t &val) -> bool {
            uint64_t val64 = 0;
            if (!parseU64(val64))
                return false;
            if (val64 > (uint64_t)std::numeric_limits<uint32_t>::max()) {
                parseError = true;
                std::cerr << it->first << ": " << argv[i] << ": malformed integer (value too large for 32b)\n";
                return false;
            }
            val = (uint32_t)val64;
            return true;
        };

        // Arg corrsponds to vISAOpt.
        // If bool, set it with the inverse of the default value
        // Else if int32,int64, or cstr, set parse argv[i+1] and set the value.
        vISAOptions vISAOpt = it->second;
        EntryType type = m_vISAOptions.getType(vISAOpt);
        switch (type) {
        case ET_BOOL: {
            bool val = !m_vISAOptions.getDefaultBool(vISAOpt);
            m_vISAOptions.setBool(vISAOpt, val);
            m_vISAOptions.setArgSetByUser(vISAOpt);
            break;
        }
        case ET_INT32:
        case ET_INT64:
        case ET_CSTR:
        case ET_2xINT32:
            i++;
            if (i >= argc) {
                const char *errorMsg = m_vISAOptions.getErrorMsg(vISAOpt);
                std::cerr << it->first << ": argument expected: " << errorMsg << "\n";
                return false;
            }
            switch (type) {
            case ET_INT32: {
                uint32_t val = 0;
                if (!parseU32(val)) {
                    return false;
                }
                m_vISAOptions.setUint32(vISAOpt, val);
                m_vISAOptions.setArgSetByUser(vISAOpt);
                break;
            }
            case ET_INT64: {
                uint64_t val = 0;
                if (!parseU64(val)) {
                    return false;
                }
                m_vISAOptions.setUint64(vISAOpt, val);
                m_vISAOptions.setArgSetByUser(vISAOpt);
                break;
            }
            case ET_CSTR:
                m_vISAOptions.setCstr(vISAOpt, argv[i]);
                m_vISAOptions.setArgSetByUser(vISAOpt);
                break;
            case ET_2xINT32: {
                uint32_t hi32 = 0;
                if (!parseU32(hi32)) {
                    return false;
                }
                i++;
                if (i >= argc) {
                    std::cerr << it->first << ": vISA option type ET_2xINT32 requires two integer arguments\n";
                    return false;
                }
                uint32_t lo32 = 0;
                if (!parseU32(lo32)) {
                    return false;
                }
                uint64_t val64 = ((uint64_t)hi32 << 32) | (uint64_t)lo32;
                m_vISAOptions.setUint64(vISAOpt, val64);
                m_vISAOptions.setArgSetByUser(vISAOpt);
                break;
            }
            default:
                assert(0 && "Bad option type");
            }
            break;
        default:
            assert(0 && "Bad option type");
        } // switch
        if (parseError)
            return false;
    }


    // Dependent arguments.
    // If setting an argument triggers more arguments, these should be
    // set here.
    if (m_vISAOptions.isArgSetByUser(vISA_LocalRA)) {
        m_vISAOptions.setBool(vISA_LocalBankConflictReduction, false);
    }
    if (m_vISAOptions.isArgSetByUser(vISA_LocalRARoundRobin)) {
        m_vISAOptions.setBool(vISA_LocalBankConflictReduction, false);
    }
    if (m_vISAOptions.isArgSetByUser(vISA_Debug)) {
        m_vISAOptions.setBool(vISA_EnableSendFusion, false);
        m_vISAOptions.setBool(vISA_LocalScheduling, false);
        m_vISAOptions.setBool(vISA_Compaction, false);
        m_vISAOptions.setBool(vISA_LocalCopyProp, false);
        m_vISAOptions.setBool(vISA_LocalFlagOpt, false);
        m_vISAOptions.setBool(vISA_LocalMACopt, false);
        m_vISAOptions.setBool(vISA_LocalDefHoist, false);
        m_vISAOptions.setBool(vISA_LocalCleanMessageHeader, false);
        m_vISAOptions.setBool(vISA_LocalRenameRegister, false);
        m_vISAOptions.setBool(vISA_IPA, false);
        m_vISAOptions.setBool(vISA_FoldAddrImmed, false);
        m_vISAOptions.setBool(vISA_MergeScalar, false);
        m_vISAOptions.setBool(vISA_EnableMACOpt, false);
        m_vISAOptions.setBool(vISA_DisableleHFOpt, true);
        m_vISAOptions.setBool(vISA_LVN, false);
        m_vISAOptions.setBool(vISA_LocalRARoundRobin, false);
        m_vISAOptions.setBool(vISA_LocalBankConflictReduction, false);
        m_vISAOptions.setBool(vISA_RoundRobin, false);
        m_vISAOptions.setBool(vISA_SpiltLLR, false);
        m_vISAOptions.setBool(vISA_preRA_Schedule, false);
        m_vISAOptions.setBool(vISA_SplitGRFAlignedScalar, false);
        m_vISAOptions.setBool(vISA_SkipRedundantFillInRMW, false);
        m_vISAOptions.setBool(vISA_Debug, true);
    }
    if (m_vISAOptions.isArgSetByUser(vISA_Stepping)) {
        const char *stepping = m_vISAOptions.getCstr(vISA_Stepping);
        if (stepping == nullptr)
        {
            return false;
        }
        int status = SetStepping(stepping);
        if (status != 0) {
            std::cerr << "unrecognized stepping string: " << stepping << "\n";
            return false;
        }
    }
    if (m_vISAOptions.isArgSetByUser(vISA_DumpPasses)) {
        m_vISAOptions.setBool(vISA_DumpDotAll, true);
    }
    if (m_vISAOptions.isArgSetByUser(vISA_DumpDotAll)) {
        m_vISAOptions.setBool(vISA_DumpDot, true);
        m_vISAOptions.setBool(vISA_DumpSchedule, true);
        m_vISAOptions.setBool(vISA_DumpDagDot, true);
        m_vISAOptions.setBool(vISA_DumpPasses, true);
    }
    if (m_vISAOptions.isArgSetByUser(vISA_3DOption)) {
        target = VISA_3D;
    }

    if (m_vISAOptions.isArgSetByUser(vISA_ReservedGRFNum)) {
        if (m_vISAOptions.getUint32(vISA_ReservedGRFNum)) {
            m_vISAOptions.setBool(vISA_LocalBankConflictReduction, false);
        }
    }

    if (m_vISAOptions.isArgSetByUser(vISA_ForceSpills)) {
        m_vISAOptions.setBool(vISA_LocalRA, false);
    }

    if (m_vISAOptions.isArgSetByUser(vISA_HashVal)) {
        m_vISAOptions.setBool(vISA_InsertHashMovs, true);
    }

    if (m_vISAOptions.isArgSetByUser(vISA_CISAbinary)) {
        const char *cisaBinary = m_vISAOptions.getCstr(vISA_CISAbinary);
        m_vISAOptions.setCstr(vISA_ISAASMNamesFile, cisaBinary);
        m_vISAOptions.setBool(vISA_IsaasmNamesFileUsed, true);
        m_vISAOptions.setBool(vISA_isParseMode, true);
    }

    if (m_vISAOptions.isArgSetByUser(vISA_ISAASMNamesFile)) {
        m_vISAOptions.setBool(vISA_GenIsaAsmList, true);
        m_vISAOptions.setBool(vISA_IsaasmNamesFileUsed, true);
    }

    if (m_vISAOptions.isArgSetByUser(vISA_Platform)) {
        const char *platformStr = m_vISAOptions.getCstr(vISA_Platform);
        if (platformStr == nullptr)
        {
            return false;
        }

        TARGET_PLATFORM platform = vISA::PlatformInfo::getVisaPlatformFromStr(platformStr);
        if (platform == GENX_NONE) {
            std::cerr << platformStr << ": unrecognized platform string\n" <<
                "supported platforms are: " << makePlatformsString() << "\n";
            return false;
        }
        m_vISAOptions.setUint32(vISA_PlatformSet, platform);
        if (GetStepping() == Step_A)
        {
            if (platform == GENX_TGLLP)
            {
                m_vISAOptions.setBool(vISA_HasEarlyGRFRead, true);
            }
        }
    }

    if (m_vISAOptions.isArgSetByUser(vISA_GetvISABinaryName)) {
        m_vISAOptions.setBool(vISA_OutputvISABinaryName, true);
    }

    if (m_vISAOptions.isArgSetByUser(vISA_LabelStr)) {
        ASSERT_USER(strlen(m_vISAOptions.getCstr(vISA_LabelStr))
                    < MAX_LABEL_STR_LENGTH,
                    "String length for unique labels is too long. Should be no larger than 8.");
        m_vISAOptions.setBool(vISA_UniqueLabels, true);         //uniqueLabels = true;
    }
    if (m_vISAOptions.isArgSetByUser(VISA_AsmFileName)) {
        m_vISAOptions.setBool(vISA_AsmFileNameOverridden, true);
    }

    if (m_vISAOptions.isArgSetByUser(vISA_DisableSpillCoalescing))
    {
        m_vISAOptions.setBool(vISA_DisableSpillCoalescing, true);
    }

    if (m_vISAOptions.isArgSetByUser(vISA_forceDebugSWSB))
    {
        m_vISAOptions.setUint32(vISA_SWSBInstStall, 0);
        m_vISAOptions.setUint32(vISA_SWSBTokenBarrier, 0);
    }

    if (m_vISAOptions.getBool(vISA_dumpVISAOptionsAll)) {
        dump();
    }

    return true;
}

void Options::showUsage(std::ostream& output)
{
    output << "USAGE: GenX_IR <InputFilename.isa> {Option List}\n";
    output << "Converts a vISA assembly or binary file into Gen binary or assembly\n";
    output << "Options:\n";
#ifndef DLL_MODE
    output <<
        "    -platform PLT                   - Gen platform to use (required)\n"
        "        supported platforms are: " << makePlatformsString() << "\n"
        "    -binary                         - Emit the binary code (CISA binary .isa and GEN bits as .dat).\n";
#endif
    output <<
        "    -output                         - Emit GEN assembly code to a file (.asm).\n"
        "    -dumpcommonisa                  - Emit CISA assembly (.visaasm).\n"
        "    -noschedule                     - Turn off code scheduling.\n"
        "    -nocompaction                   - Turn off binary compaction.\n"
        "    -outputCisaBinaryName <PATH>    - name for the CISA binary file.\n"
        "    -... many more; use -dumpVisaOptionsAll\n"
        "\n";
}

// This converts enum vISA_option to "vISA_option" so we can print it.
void Options::initialize_vISAOptionsToStr(void) {
   #undef DEF_VISA_OPTION
   #define DEF_VISA_OPTION(ENUM, TYPE, STR, ERROR_MSG, DEFAULT_VAL) \
    vISAOptionsToStr[ENUM] = #ENUM;
   #include "VISAOptionsDefs.h"
}

// Populate argToOption[] map
void Options::initializeArgToOption(void) {
   #undef DEF_VISA_OPTION
   #define DEF_VISA_OPTION(ENUM, TYPE, STR, ERROR_MSG, DEFAULT_VAL) \
    argToOption[STR] = ENUM;
   #include "VISAOptionsDefs.h"
}

// Initialize:
// 1. The strings "-someOption"
// 2. The default values
// 3. Set the value = default value
void Options::initialize_m_vISAOptions(void) {
    // I am not sure why, but without const_cast<> I am getting warnings
    // that "this" is const
    auto *Opts = const_cast<VISAOptionsDB *>(&m_vISAOptions);

    #undef DEF_VISA_OPTION
    #define DEF_VISA_OPTION(ENUM, TYPE, STR, ERROR_MSG, DEFAULT_VAL)    \
        Opts->setArgStr(ENUM, STR);                                     \
        Opts->setType(ENUM, TYPE);                                      \
        Opts->setErrorMsg(ENUM, ERROR_MSG);                             \
        switch ((TYPE)) {                                               \
        case ET_BOOL:                                                   \
            /* The casts are here to avoid type conversion warnings */  \
            Opts->setDefaultBool(ENUM, (bool) (DEFAULT_VAL));           \
            Opts->setBool(ENUM, (bool) (DEFAULT_VAL));                  \
            break;                                                      \
        case ET_INT32:                                                  \
            /* The casts are here to avoid type conversion warnings */  \
            Opts->setDefaultUint32(ENUM, (uint32_t) (uint64_t) (DEFAULT_VAL)); \
            Opts->setUint32(ENUM, (uint32_t) (uint64_t) (DEFAULT_VAL)); \
            break;                                                      \
        case ET_INT64:                                                  \
        case ET_2xINT32:                                                \
            /* The casts are here to avoid type conversion warnings */  \
            Opts->setDefaultUint64(ENUM, (uint64_t) (DEFAULT_VAL));     \
            Opts->setUint64(ENUM, (uint64_t) (DEFAULT_VAL));            \
            break;                                                      \
        case ET_CSTR:                                                   \
            /* The casts are here to avoid type conversion warnings */  \
            Opts->setDefaultCstr(ENUM, (const char *) (DEFAULT_VAL));   \
            Opts->setCstr(ENUM, (const char *) (DEFAULT_VAL));          \
            break;                                                      \
        default:                                                        \
            assert(0 && "Bad TYPE");                                    \
        }
    #include "include/VISAOptionsDefs.h"
}

// This is to check whether an option of any type is set.
// It is useful for Cstr or Int arguments because getOption() will give us
// the value, not whether they are set or not.
bool Options::isOptionSetByUser(vISAOptions option) const
{
    ASSERT_USER(option < vISA_NUM_OPTIONS, "Option value is outside of range.");
    return m_vISAOptions.isArgSetByUser(option);
}

void Options::getOption(vISAOptions option, bool &value) const
{
    ASSERT_USER(option < vISA_NUM_OPTIONS, "Option value is outside of range.");
    value = m_vISAOptions.getBool(option);
}

bool Options::getOption(vISAOptions option) const
{
    ASSERT_USER(option < vISA_NUM_OPTIONS, "Option value is outside of range.");
    return m_vISAOptions.getBool(option);
}

void Options::getOption(vISAOptions option, const char*& buf) const
{
    ASSERT_USER(option < vISA_NUM_OPTIONS, "Option value is outside of range.");
    buf = m_vISAOptions.getCstr(option);
}

const char *Options::getOptionCstr(vISAOptions option) const
{
    ASSERT_USER(option < vISA_NUM_OPTIONS, "Option value is outside of range.");
    return m_vISAOptions.getCstr(option);
}

uint32_t Options::getuInt32Option(vISAOptions option) const
{
    return m_vISAOptions.getUint32(option);
}

uint64_t Options::getuInt64Option(vISAOptions option) const
{
    if (option == vISAOptions::vISA_HashVal)
        return m_vISAOptions.getUint64(vISA_HashVal);
    else if (option == vISAOptions::vISA_HashVal1)
        return m_vISAOptions.getUint64(vISA_HashVal1);

    return 0;
}

void Options::setOptionInternally(vISAOptions option, bool val)
{
    m_vISAOptions.setBool(option, val);

#if (defined(_DEBUG) || defined(_INTERNAL))
    if (m_vISAOptions.getBool(vISA_dumpVISAOptions)) {
        std::cerr << std::setw(30) << vISAOptionsToStr[option] << " ";
        m_vISAOptions.dump(option);
        std::cerr << "\n";
    }
#endif
}

void Options::setOptionInternally(vISAOptions option, uint32_t value)
{
    m_vISAOptions.setUint32(option, value);

#if (defined(_DEBUG) || defined(_INTERNAL))
    if (m_vISAOptions.getBool(vISA_dumpVISAOptions)) {
        std::cerr << std::setw(30) << vISAOptionsToStr[option] << " ";
        m_vISAOptions.dump(option);
        std::cerr << "\n";
    }
#endif
}

void Options::setOptionInternally(vISAOptions option, const char* str)
{
    m_vISAOptions.setCstr(option, str);

#if (defined(_DEBUG) || defined(_INTERNAL))
    if (m_vISAOptions.getBool(vISA_dumpVISAOptions)) {
        std::cerr << std::setw(30) << vISAOptionsToStr[option] << " ";
        m_vISAOptions.dump(option);
        std::cerr << "\n";
    }
#endif
}

void Options::setOption(vISAOptions option, bool val)
{
    m_vISAOptions.setArgSetByUser(option);
    setOptionInternally(option, val);
}

void Options::setOption(vISAOptions option, uint32_t value)
{
    m_vISAOptions.setArgSetByUser(option);
    setOptionInternally(option, value);
}

void Options::setOption(vISAOptions option, const char* str)
{
    m_vISAOptions.setArgSetByUser(option);
    setOptionInternally(option, str);
}

//
// return full set of arguments ever set by user, either through
// string options or the various setOptions()
//
std::string Options::getFullArgString() const
{
    std::stringstream args;
    // Collect all user-set options.
    // This is for igc. When igc invokes vISA, it sets options
    // via setOption() api instead of options string, thus leave
    // argString empty. Here, we re-generate this options strings
    //
    // If a flag has no name (it happens), skip it!
    for (int i = vISA_OPTIONS_UNINIT + 1; i < vISA_NUM_OPTIONS; ++i)
    {
        vISAOptions o = (vISAOptions)i;
        if (isOptionSetByUser(o))
        {
            EntryType type = m_vISAOptions.getType(o);
            switch (type) {
            case ET_BOOL:
                if (m_vISAOptions.getBool(o) != m_vISAOptions.getDefaultBool(o))
                {
                    // Boolean option means the reverse of the default!
                    // (Probably should avoid such reverse handling)
                    const char* argName = m_vISAOptions.getArgStr(o);
                    if (argName && argName[0] != 0)
                    {
                        args << argName << " ";
                    }
                }
                break;
            case ET_INT32:
            {
                const char* argName = m_vISAOptions.getArgStr(o);
                if (argName && argName[0] != 0)
                {
                    args << argName << " " << m_vISAOptions.getUint32(o) << " ";
                }
                break;
            }
            case ET_INT64:
            {
                const char* argName = m_vISAOptions.getArgStr(o);
                if (argName && argName[0] != 0)
                {
                    args << argName << " " << m_vISAOptions.getUint64(o) << " ";
                }
                break;
            }
            case ET_2xINT32:
            {
                uint32_t lo32, hi32;
                uint64_t val = m_vISAOptions.getUint64(o);
                lo32 = (uint32_t)val;
                hi32 = (uint32_t)(val >> 32);

                const char* argName = m_vISAOptions.getArgStr(o);
                if (argName && argName[0] != 0)
                {
                    args << argName << " " << hi32 << " " << lo32 << " ";
                }
            }
            break;
            case ET_CSTR:
            {
                const char* argName = m_vISAOptions.getArgStr(o);
                if (!argName || argName[0] == 0)
                {
                    break;
                }
                args << argName << " ";
                const char *sval = m_vISAOptions.getCstr(o);
                // Careful not to emit empty strings
                // ... -string_opt   -next_opt
                //                 ^ empty string value for -string_opt
                // We need to put something so poor fool (me) that comes along
                // and tries to parse the .full_options line doesn't get
                // -string_opt parsing -next_opt as the value
                // Instead produce:
                // ... -string_opt ""  -next_opt"
                if (sval == nullptr || *sval == 0) {
                    args << "\"\""; // emit escaped so tokenization in next parse works
                } else {
                    std::string s = sval;
                    if (s.find(' ') != std::string::npos) {
                        // if there's spaces escape them
                        //   -string_opt "foo bar" -next_opt
                        args << "\"" << s << "\"";
                    } else {
                        args << s;
                    }
                }
                args << " ";
                break;
            }
            default:
                assert(false && "Invalid vISA option type!");
                args << "UNDEFINED ";
                break;
            }
        }
    }
    return args.str();
}

//
// this returns the options string explicitly passed in by user
//
std::stringstream& Options::getUserArgString()
{
    return argString;
}

std::string Options::getEncoderOutputFile() const
{
    const char * encoderOutputFile = m_vISAOptions.getCstr(vISA_encoderFile);
    if (encoderOutputFile != nullptr)
    {
        return std::string(encoderOutputFile);
    }
    else
    {
        return std::string("");
    }
}

//
// This is to read visa options from environment variable: VISA_OPTIONS
// Options string is blank-seperated, just like ones used in genx_ir.
// For example,
//    VISA_OPTIONS=-dotAll -noAccSub
//  or
//    VISA_OPTIONS="-dotAll -noAccSub"
//
// The option in this environment variable overrides the previous one!
// This is intended for non visa standalone use only (not via genx_ir).
//
void Options::getOptionsFromEV()
{
#if defined(_DEBUG) || defined(_INTERNAL)
    const char* visaOptionsEV = "VISA_OPTIONS";
    const char* pVisaEV = getenv(visaOptionsEV);
    if (!pVisaEV)
    {
        return;
    }

    std::cerr << "VISA Environment Variable in effect:\n"
        << visaOptionsEV << " = " << pVisaEV << "\n";

    std::string ostr(pVisaEV);
    // Remove leading/trailing quote if present
    size_t pos = ostr.find_first_not_of(' ');
    char firstC = ostr.at(pos);
    if (firstC == '"' || firstC == '\'')
    {
        ostr.at(pos) = ' ';
        size_t p0 = ostr.find_last_not_of(" ");
        if (firstC == ostr.at(p0))
        {
            ostr.at(p0) = ' ';
        }
        else
        {
            std::cerr << "    Environment variable's leading and trailing quote ("
                << firstC << ", " << ostr.at(p0) << ") : not matched!  Ignored!\n";
            return;
        }
        // pos to the fist non-blank valid char
        pos = ostr.find_first_not_of(' ', pos + 1);
    }
    std::vector<std::string> flags;
    size_t currPos = pos;
    while (currPos != std::string::npos)
    {
        pos = ostr.find_first_of(' ', currPos);
        flags.emplace_back(ostr.substr(currPos, pos - currPos));
        currPos = ostr.find_first_not_of(' ', pos);  // pos can be npos!
    }

    int sz = (int)flags.size();
    if (sz <= 0) {
        return;
    }
    char** argvars = new char* [sz];
    const char** pargs = (const char**)argvars;
    for (int i = 0; i < sz; ++i)
    {
        pargs[i] = flags[i].c_str();
    }
    parseOptions(sz, pargs);
    delete [] argvars;
    return;
#endif
}

void Options::dump(void) const {
    m_vISAOptions.dump();
}

Options::Options() {
    m_vISAOptions = VISAOptionsDB(this);

    target = VISA_CM;

    initialize_vISAOptionsToStr();
    initializeArgToOption();
    initialize_m_vISAOptions();
}
