//===-- MICmdArgValString.cpp -----------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// In-house headers:
#include "MICmdArgValString.h"
#include "MICmdArgContext.h"

//++ ------------------------------------------------------------------------------------
// Details: CMICmdArgValString constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdArgValString::CMICmdArgValString()
    : m_bHandleQuotedString(false)
    , m_bAcceptNumbers(false)
    , m_bHandleDirPaths(false)
    , m_bHandleAnything(false)
{
}

//++ ------------------------------------------------------------------------------------
// Details: CMICmdArgValString constructor.
// Type:    Method.
// Args:    vbAnything  - (R) True = Parse a string and accept anything, false = do not accept anything.
// Return:  None.
// Throws:  None.
//--
CMICmdArgValString::CMICmdArgValString(const bool vbAnything)
    : m_bHandleQuotedString(vbAnything ? true : false)
    , m_bAcceptNumbers(false)
    , m_bHandleDirPaths(false)
    , m_bHandleAnything(vbAnything)
{
}

//++ ------------------------------------------------------------------------------------
// Details: CMICmdArgValString constructor.
// Type:    Method.
// Args:    vbHandleQuotes      - (R) True = Parse a string surrounded by quotes spaces are not delimiters, false = only text up to
// next delimiting space character.
//          vbAcceptNumbers     - (R) True = Parse a string and accept as a number if number, false = numbers not recognised
// as string types.
//          vbHandleDirPaths    - (R) True = Parse a string and accept as a file path if a path, false = file paths are not
// recognised as string types.
// Return:  None.
// Throws:  None.
//--
CMICmdArgValString::CMICmdArgValString(const bool vbHandleQuotes, const bool vbAcceptNumbers, const bool vbHandleDirPaths)
    : m_bHandleQuotedString(vbHandleQuotes)
    , m_bAcceptNumbers(vbAcceptNumbers)
    , m_bHandleDirPaths(vbHandleDirPaths)
    , m_bHandleAnything(false)
{
}

//++ ------------------------------------------------------------------------------------
// Details: CMICmdArgValString constructor.
// Type:    Method.
// Args:    vrArgName       - (R) Argument's name to search by.
//          vbMandatory     - (R) True = Yes must be present, false = optional argument.
//          vbHandleByCmd   - (R) True = Command processes *this option, false = not handled.
//          vbHandleQuotes  - (R) True = Parse a string surrounded by quotes spaces are not delimiters, false = only text up to
// next delimiting space character. (Dflt = false)
//          vbAcceptNumbers - (R) True = Parse a string and accept as a number if number, false = numbers not recognised as
// string types. (Dflt = false)
// Return:  None.
// Throws:  None.
//--
CMICmdArgValString::CMICmdArgValString(const CMIUtilString &vrArgName, const bool vbMandatory, const bool vbHandleByCmd,
                                       const bool vbHandleQuotes /* = false */, const bool vbAcceptNumbers /* = false */)
    : CMICmdArgValBaseTemplate(vrArgName, vbMandatory, vbHandleByCmd)
    , m_bHandleQuotedString(vbHandleQuotes)
    , m_bAcceptNumbers(vbAcceptNumbers)
    , m_bHandleDirPaths(false)
    , m_bHandleAnything(false)
{
}

//++ ------------------------------------------------------------------------------------
// Details: CMICmdArgValString destructor.
// Type:    Overridden.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdArgValString::~CMICmdArgValString()
{
}

//++ ------------------------------------------------------------------------------------
// Details: Parse the command's argument options string and try to extract the value *this
//          argument is looking for.
// Type:    Overridden.
// Args:    vrwArgContext   - (RW) The command's argument options string.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdArgValString::Validate(CMICmdArgContext &vrwArgContext)
{
    if (vrwArgContext.IsEmpty())
        return m_bMandatory ? MIstatus::failure : MIstatus::success;

    if (m_bHandleQuotedString)
        return ValidateQuotedText(vrwArgContext);

    return ValidateSingleText(vrwArgContext);
}

//++ ------------------------------------------------------------------------------------
// Details: Parse the command's argument options string and try to extract only the next
//          word delimited by the next space.
// Type:    Method.
// Args:    vrwArgContext   - (RW) The command's argument options string.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdArgValString::ValidateSingleText(CMICmdArgContext &vrwArgContext)
{
    const CMIUtilString::VecString_t vecOptions(vrwArgContext.GetArgs());
    CMIUtilString::VecString_t::const_iterator it = vecOptions.begin();
    while (it != vecOptions.end())
    {
        const CMIUtilString &rArg(*it);
        if (IsStringArg(rArg))
        {
            m_bFound = true;

            if (vrwArgContext.RemoveArg(rArg))
            {
                m_bValid = true;
                m_argValue = rArg.StripSlashes();
                return MIstatus::success;
            }
            else
                return MIstatus::failure;
        }

        // Next
        ++it;
    }

    return MIstatus::failure;
}

//++ ------------------------------------------------------------------------------------
// Details: Parse the command's argument options string and try to extract all the words
//          between quotes then delimited by the next space.
// Type:    Method.
// Args:    vrwArgContext   - (RW) The command's argument options string.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdArgValString::ValidateQuotedText(CMICmdArgContext &vrwArgContext)
{
    const CMIUtilString::VecString_t vecOptions(vrwArgContext.GetArgs());
    if (vecOptions.size() == 0)
        return MIstatus::failure;

    const CMIUtilString &rArg(vecOptions[0]);
    if (!IsStringArg(rArg))
        return MIstatus::failure;

    m_bFound = true;

    if (vrwArgContext.RemoveArg(rArg))
    {
        m_bValid = true;
        const char cQuote = '"';
        m_argValue = rArg.Trim(cQuote).StripSlashes();
        return MIstatus::success;
    }

    return MIstatus::failure;
}

//++ ------------------------------------------------------------------------------------
// Details: Examine the string and determine if it is a valid string type argument.
// Type:    Method.
// Args:    vrTxt   - (R) Some text.
// Return:  bool    - True = yes valid arg, false = no.
// Throws:  None.
//--
bool
CMICmdArgValString::IsStringArg(const CMIUtilString &vrTxt) const
{
    if (m_bHandleQuotedString)
        return (IsStringArgQuotedText(vrTxt) || IsStringArgQuotedTextEmbedded(vrTxt) || IsStringArgQuotedQuotedTextEmbedded(vrTxt) ||
                IsStringArgSingleText(vrTxt)); // Still test for this as could just be one word still

    return IsStringArgSingleText(vrTxt);
}

//++ ------------------------------------------------------------------------------------
// Details: Examine the string and determine if it is a valid string type argument or
//          option value. If the string looks like a long option, short option, a thread
//          group ID or just a number it is rejected as a string type value. There is an
//          option to allow the string to accept a number as a string type.
// Type:    Method.
// Args:    vrTxt   - (R) Some text.
// Return:  bool    - True = yes valid argument value, false = something else.
// Throws:  None.
//--
bool
CMICmdArgValString::IsStringArgSingleText(const CMIUtilString &vrTxt) const
{
    if (!m_bHandleDirPaths)
    {
        // Look for directory file paths, if found reject
        const bool bHavePosSlash = (vrTxt.find('/') != std::string::npos);
        const bool bHaveBckSlash = (vrTxt.find('\\') != std::string::npos);
        if (bHavePosSlash || bHaveBckSlash)
            return false;
    }

    // Look for --someLongOption, if found reject
    if (0 == vrTxt.find("--"))
        return false;

    // Look for -f type short options, if found reject
    if ((0 == vrTxt.find('-')) && (vrTxt.length() == 2))
        return false;

    // Look for thread group i1 i2 i3...., if found reject
    if ((vrTxt.find('i') == 0) && ::isdigit(vrTxt[1]))
        return false;

    // Look for numbers, if found reject
    if (!m_bAcceptNumbers && vrTxt.IsNumber())
        return false;

    return true;
}

//++ ------------------------------------------------------------------------------------
// Details: Examine the string and determine if it is a valid string type argument.
//          Take into account quotes surrounding the text. Note this function falls
//          through to IsStringArgSingleText() should the criteria match fail.
// Type:    Method.
// Args:    vrTxt   - (R) Some text.
// Return:  bool    - True = yes valid arg, false = no.
// Throws:  None.
//--
bool
CMICmdArgValString::IsStringArgQuotedText(const CMIUtilString &vrTxt) const
{
    // Accept anything as string word
    if (m_bHandleAnything)
        return true;

    // CODETAG_QUOTEDTEXT_SIMILAR_CODE
    const char cQuote = '"';
    const size_t nPos = vrTxt.find(cQuote);
    if (nPos == std::string::npos)
        return false;

    // Is one and only quote at end of the string
    if (nPos == (vrTxt.length() - 1))
        return false;

    // Quote must be the first character in the string or be preceded by a space
    // Also check for embedded string formating quote
    const char cBckSlash = '\\';
    const char cSpace = ' ';
    if ((nPos > 1) && (vrTxt[nPos - 1] == cBckSlash) && (vrTxt[nPos - 2] != cSpace))
    {
        return false;
    }
    if ((nPos > 0) && (vrTxt[nPos - 1] != cSpace))
        return false;

    // Need to find the other quote
    const size_t nPos2 = vrTxt.rfind(cQuote);
    if (nPos2 == std::string::npos)
        return false;

    // Make sure not same quote, need two quotes
    if (nPos == nPos2)
        return MIstatus::failure;

    return true;
}

//++ ------------------------------------------------------------------------------------
// Details: Examine the string and determine if it is a valid string type argument.
//          Take into account quotes surrounding the text. Take into account string format
//          embedded quotes surrounding the text i.e. "\\\"%5d\\\"". Note this function falls
//          through to IsStringArgQuotedText() should the criteria match fail.
// Type:    Method.
// Args:    vrTxt   - (R) Some text.
// Return:  bool    - True = yes valid arg, false = no.
// Throws:  None.
//--
bool
CMICmdArgValString::IsStringArgQuotedTextEmbedded(const CMIUtilString &vrTxt) const
{
    // CODETAG_QUOTEDTEXT_SIMILAR_CODE
    const char cBckSlash = '\\';
    const size_t nPos = vrTxt.find(cBckSlash);
    if (nPos == std::string::npos)
        return false;

    // Slash must be the first character in the string or be preceded by a space
    const char cSpace = ' ';
    if ((nPos > 0) && (vrTxt[nPos - 1] != cSpace))
        return false;

    // Need to find the other matching slash
    const size_t nPos2 = vrTxt.rfind(cBckSlash);
    if (nPos2 == std::string::npos)
        return false;

    // Make sure not same back slash, need two slashes
    if (nPos == nPos2)
        return MIstatus::failure;

    return false;
}

//++ ------------------------------------------------------------------------------------
// Details: Examine the string and determine if it is a valid string type argument.
//          Take into account quotes surrounding the text. Take into account string format
//          embedded quotes surrounding the text i.e. "\\\"%5d\\\"". Note this function falls
//          through to IsStringArgQuotedTextEmbedded() should the criteria match fail.
// Type:    Method.
// Args:    vrTxt   - (R) Some text.
// Return:  bool    - True = yes valid arg, false = no.
// Throws:  None.
//--
bool
CMICmdArgValString::IsStringArgQuotedQuotedTextEmbedded(const CMIUtilString &vrTxt) const
{
    const size_t nPos = vrTxt.find("\"\\\"");
    if (nPos == std::string::npos)
        return false;

    const size_t nPos2 = vrTxt.rfind("\\\"\"");
    if (nPos2 == std::string::npos)
        return false;

    const size_t nLen = vrTxt.length();
    if ((nLen > 5) && ((nPos + 2) == (nPos2 - 2)))
        return false;

    return true;
}
