Args.cpp   [plain text]


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

#include "lldb/lldb-python.h"

// C Includes
#include <getopt.h>
#include <cstdlib>
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Interpreter/Args.h"
#include "lldb/Core/FormatManager.h"
#include "lldb/Core/Stream.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Interpreter/Options.h"
#include "lldb/Interpreter/CommandReturnObject.h"

using namespace lldb;
using namespace lldb_private;

//----------------------------------------------------------------------
// Args constructor
//----------------------------------------------------------------------
Args::Args (const char *command) :
    m_args(),
    m_argv(),
    m_args_quote_char()
{
    if (command)
        SetCommandString (command);
}


Args::Args (const char *command, size_t len) :
    m_args(),
    m_argv(),
    m_args_quote_char()
{
    if (command && len)
        SetCommandString (command, len);
}

//----------------------------------------------------------------------
// We have to be very careful on the copy constructor of this class
// to make sure we copy all of the string values, but we can't copy the
// rhs.m_argv into m_argv since it will point to the "const char *" c 
// strings in rhs.m_args. We need to copy the string list and update our
// own m_argv appropriately. 
//----------------------------------------------------------------------
Args::Args (const Args &rhs) :
    m_args (rhs.m_args),
    m_argv (),
    m_args_quote_char(rhs.m_args_quote_char)
{
    UpdateArgvFromArgs();
}

//----------------------------------------------------------------------
// We have to be very careful on the copy constructor of this class
// to make sure we copy all of the string values, but we can't copy the
// rhs.m_argv into m_argv since it will point to the "const char *" c 
// strings in rhs.m_args. We need to copy the string list and update our
// own m_argv appropriately. 
//----------------------------------------------------------------------
const Args &
Args::operator= (const Args &rhs)
{
    // Make sure we aren't assigning to self
    if (this != &rhs)
    {
        m_args = rhs.m_args;
        m_args_quote_char = rhs.m_args_quote_char;
        UpdateArgvFromArgs();
    }
    return *this;
}

//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
Args::~Args ()
{
}

void
Args::Dump (Stream *s)
{
    const int argc = m_argv.size();
    for (int i=0; i<argc; ++i)
    {
        s->Indent();
        const char *arg_cstr = m_argv[i];
        if (arg_cstr)
            s->Printf("argv[%i]=\"%s\"\n", i, arg_cstr);
        else
            s->Printf("argv[%i]=NULL\n", i);
    }
    s->EOL();
}

bool
Args::GetCommandString (std::string &command) const
{
    command.clear();
    int argc = GetArgumentCount();
    for (int i=0; i<argc; ++i)
    {
        if (i > 0)
            command += ' ';
        command += m_argv[i];
    }
    return argc > 0;
}

bool
Args::GetQuotedCommandString (std::string &command) const
{
    command.clear ();
    size_t argc = GetArgumentCount ();
    for (size_t i = 0; i < argc; ++i)
    {
        if (i > 0)
            command.append (1, ' ');
        char quote_char = GetArgumentQuoteCharAtIndex(i);
        if (quote_char)
        {
            command.append (1, quote_char);
            command.append (m_argv[i]);
            command.append (1, quote_char);
        }
        else
            command.append (m_argv[i]);
    }
    return argc > 0;
}

void
Args::SetCommandString (const char *command, size_t len)
{
    // Use std::string to make sure we get a NULL terminated string we can use
    // as "command" could point to a string within a large string....
    std::string null_terminated_command(command, len);
    SetCommandString(null_terminated_command.c_str());
}

void
Args::SetCommandString (const char *command)
{
    m_args.clear();
    m_argv.clear();
    m_args_quote_char.clear();

    if (command && command[0])
    {
        static const char *k_space_separators = " \t";
        static const char *k_space_separators_with_slash_and_quotes = " \t \\'\"";
        const char *arg_end = NULL;
        const char *arg_pos;
        for (arg_pos = command;
             arg_pos && arg_pos[0];
             arg_pos = arg_end)
        {
            // Skip any leading space separators
            const char *arg_start = ::strspn (arg_pos, k_space_separators) + arg_pos;
            
            // If there were only space separators to the end of the line, then
            // we're done.
            if (*arg_start == '\0')
                break;

            // Arguments can be split into multiple discontiguous pieces,
            // for example:
            //  "Hello ""World"
            // this would result in a single argument "Hello World" (without/
            // the quotes) since the quotes would be removed and there is 
            // not space between the strings. So we need to keep track of the
            // current start of each argument piece in "arg_piece_start"
            const char *arg_piece_start = arg_start;
            arg_pos = arg_piece_start;

            std::string arg;
            // Since we can have multiple quotes that form a single command
            // in a command like: "Hello "world'!' (which will make a single
            // argument "Hello world!") we remember the first quote character
            // we encounter and use that for the quote character.
            char first_quote_char = '\0';
            char quote_char = '\0';
            bool arg_complete = false;

            do
            {
                arg_end = ::strcspn (arg_pos, k_space_separators_with_slash_and_quotes) + arg_pos;

                switch (arg_end[0])
                {
                default:
                    assert (!"Unhandled case statement, we must handle this...");
                    break;

                case '\0':
                    // End of C string
                    if (arg_piece_start && arg_piece_start[0])
                        arg.append (arg_piece_start);
                    arg_complete = true;
                    break;
                    
                case '\\':
                    // Backslash character
                    switch (arg_end[1])
                    {
                        case '\0':
                            arg.append (arg_piece_start);
                            ++arg_end;
                            arg_complete = true;
                            break;

                        default:
                            if (quote_char == '\0')
                            {
                                arg.append (arg_piece_start, arg_end - arg_piece_start);
                                if (arg_end + 1 != '\0')
                                {
                                    arg.append (arg_end + 1, 1);
                                    arg_pos = arg_end + 2;
                                    arg_piece_start = arg_pos;
                                }
                            }
                            else
                                arg_pos = arg_end + 2;
                            break;
                    }
                    break;
                
                case '"':
                case '\'':
                case '`':
                    // Quote characters 
                    if (quote_char)
                    {
                        // We found a quote character while inside a quoted
                        // character argument. If it matches our current quote
                        // character, this ends the effect of the quotes. If it
                        // doesn't we ignore it.
                        if (quote_char == arg_end[0])
                        {
                            arg.append (arg_piece_start, arg_end - arg_piece_start);
                            // Clear the quote character and let parsing
                            // continue (we need to watch for things like:
                            // "Hello ""World"
                            // "Hello "World
                            // "Hello "'World'
                            // All of which will result in a single argument "Hello World"
                            quote_char = '\0'; // Note that we are no longer inside quotes
                            arg_pos = arg_end + 1; // Skip the quote character
                            arg_piece_start = arg_pos; // Note we are starting from later in the string
                        }
                        else
                        {
                            // different quote, skip it and keep going
                            arg_pos = arg_end + 1;
                        }
                    }
                    else
                    {
                        // We found the start of a quote scope.
                        // Make sure there isn't a string that precedes
                        // the start of a quote scope like:
                        // Hello" World"
                        // If so, then add the "Hello" to the arg
                        if (arg_end > arg_piece_start)
                            arg.append (arg_piece_start, arg_end - arg_piece_start);
                            
                        // Enter into a quote scope
                        quote_char = arg_end[0];
                        
                        if (first_quote_char == '\0')
                            first_quote_char = quote_char;

                        arg_pos = arg_end;
                        ++arg_pos;                 // Skip the quote character
                        arg_piece_start = arg_pos; // Note we are starting from later in the string
                        
                        // Skip till the next quote character
                        const char *end_quote = ::strchr (arg_piece_start, quote_char);
                        while (end_quote && end_quote[-1] == '\\')
                        {
                            // Don't skip the quote character if it is 
                            // preceded by a '\' character
                            end_quote = ::strchr (end_quote + 1, quote_char);
                        }
                        
                        if (end_quote)
                        {
                            if (end_quote > arg_piece_start)
                                arg.append (arg_piece_start, end_quote - arg_piece_start);

                            // If the next character is a space or the end of 
                            // string, this argument is complete...
                            if (end_quote[1] == ' ' || end_quote[1] == '\t' || end_quote[1] == '\0')
                            {
                                arg_complete = true;
                                arg_end = end_quote + 1;
                            }
                            else
                            {
                                arg_pos = end_quote + 1;
                                arg_piece_start = arg_pos;
                            }
                            quote_char = '\0';
                        }
                        else
                        {
                            // Consume the rest of the string as there was no terminating quote
                            arg.append(arg_piece_start);
                            arg_end = arg_piece_start + strlen(arg_piece_start);
                            arg_complete = true;
                        }
                    }
                    break;

                case ' ':
                case '\t':
                    if (quote_char)
                    {
                        // We are currently processing a quoted character and found
                        // a space character, skip any spaces and keep trying to find
                        // the end of the argument. 
                        arg_pos = ::strspn (arg_end, k_space_separators) + arg_end;
                    }
                    else
                    {
                        // We are not inside any quotes, we just found a space after an
                        // argument
                        if (arg_end > arg_piece_start)
                            arg.append (arg_piece_start, arg_end - arg_piece_start);
                        arg_complete = true;
                    }
                    break;
                }
            } while (!arg_complete);

            m_args.push_back(arg);
            m_args_quote_char.push_back (first_quote_char);
        }
        UpdateArgvFromArgs();
    }
}

void
Args::UpdateArgsAfterOptionParsing()
{
    // Now m_argv might be out of date with m_args, so we need to fix that
    arg_cstr_collection::const_iterator argv_pos, argv_end = m_argv.end();
    arg_sstr_collection::iterator args_pos;
    arg_quote_char_collection::iterator quotes_pos;

    for (argv_pos = m_argv.begin(), args_pos = m_args.begin(), quotes_pos = m_args_quote_char.begin();
         argv_pos != argv_end && args_pos != m_args.end();
         ++argv_pos)
    {
        const char *argv_cstr = *argv_pos;
        if (argv_cstr == NULL)
            break;

        while (args_pos != m_args.end())
        {
            const char *args_cstr = args_pos->c_str();
            if (args_cstr == argv_cstr)
            {
                // We found the argument that matches the C string in the
                // vector, so we can now look for the next one
                ++args_pos;
                ++quotes_pos;
                break;
            }
            else
            {
                quotes_pos = m_args_quote_char.erase (quotes_pos);
                args_pos = m_args.erase (args_pos);
            }
        }
    }

    if (args_pos != m_args.end())
        m_args.erase (args_pos, m_args.end());

    if (quotes_pos != m_args_quote_char.end())
        m_args_quote_char.erase (quotes_pos, m_args_quote_char.end());
}

void
Args::UpdateArgvFromArgs()
{
    m_argv.clear();
    arg_sstr_collection::const_iterator pos, end = m_args.end();
    for (pos = m_args.begin(); pos != end; ++pos)
        m_argv.push_back(pos->c_str());
    m_argv.push_back(NULL);
    // Make sure we have enough arg quote chars in the array
    if (m_args_quote_char.size() < m_args.size())
        m_args_quote_char.resize (m_argv.size());
}

size_t
Args::GetArgumentCount() const
{
    if (m_argv.empty())
        return 0;
    return m_argv.size() - 1;
}

const char *
Args::GetArgumentAtIndex (size_t idx) const
{
    if (idx < m_argv.size())
        return m_argv[idx];
    return NULL;
}

char
Args::GetArgumentQuoteCharAtIndex (size_t idx) const
{
    if (idx < m_args_quote_char.size())
        return m_args_quote_char[idx];
    return '\0';
}

char **
Args::GetArgumentVector()
{
    if (!m_argv.empty())
        return (char **)&m_argv[0];
    return NULL;
}

const char **
Args::GetConstArgumentVector() const
{
    if (!m_argv.empty())
        return (const char **)&m_argv[0];
    return NULL;
}

void
Args::Shift ()
{
    // Don't pop the last NULL terminator from the argv array
    if (m_argv.size() > 1)
    {
        m_argv.erase(m_argv.begin());
        m_args.pop_front();
        if (!m_args_quote_char.empty())
            m_args_quote_char.erase(m_args_quote_char.begin());
    }
}

const char *
Args::Unshift (const char *arg_cstr, char quote_char)
{
    m_args.push_front(arg_cstr);
    m_argv.insert(m_argv.begin(), m_args.front().c_str());
    m_args_quote_char.insert(m_args_quote_char.begin(), quote_char);
    return GetArgumentAtIndex (0);
}

void
Args::AppendArguments (const Args &rhs)
{
    const size_t rhs_argc = rhs.GetArgumentCount();
    for (size_t i=0; i<rhs_argc; ++i)
        AppendArgument(rhs.GetArgumentAtIndex(i));
}

void
Args::AppendArguments (const char **argv)
{
    if (argv)
    {
        for (uint32_t i=0; argv[i]; ++i)
            AppendArgument(argv[i]);
    }
}

const char *
Args::AppendArgument (const char *arg_cstr, char quote_char)
{
    return InsertArgumentAtIndex (GetArgumentCount(), arg_cstr, quote_char);
}

const char *
Args::InsertArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char)
{
    // Since we are using a std::list to hold onto the copied C string and
    // we don't have direct access to the elements, we have to iterate to
    // find the value.
    arg_sstr_collection::iterator pos, end = m_args.end();
    size_t i = idx;
    for (pos = m_args.begin(); i > 0 && pos != end; ++pos)
        --i;

    pos = m_args.insert(pos, arg_cstr);
    
    if (idx >= m_args_quote_char.size())
    {
        m_args_quote_char.resize(idx + 1);
        m_args_quote_char[idx] = quote_char;
    }
    else
        m_args_quote_char.insert(m_args_quote_char.begin() + idx, quote_char);
    
    UpdateArgvFromArgs();
    return GetArgumentAtIndex(idx);
}

const char *
Args::ReplaceArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char)
{
    // Since we are using a std::list to hold onto the copied C string and
    // we don't have direct access to the elements, we have to iterate to
    // find the value.
    arg_sstr_collection::iterator pos, end = m_args.end();
    size_t i = idx;
    for (pos = m_args.begin(); i > 0 && pos != end; ++pos)
        --i;

    if (pos != end)
    {
        pos->assign(arg_cstr);
        assert(idx < m_argv.size() - 1);
        m_argv[idx] = pos->c_str();
        if (idx >= m_args_quote_char.size())
            m_args_quote_char.resize(idx + 1);
        m_args_quote_char[idx] = quote_char;
        return GetArgumentAtIndex(idx);
    }
    return NULL;
}

void
Args::DeleteArgumentAtIndex (size_t idx)
{
    // Since we are using a std::list to hold onto the copied C string and
    // we don't have direct access to the elements, we have to iterate to
    // find the value.
    arg_sstr_collection::iterator pos, end = m_args.end();
    size_t i = idx;
    for (pos = m_args.begin(); i > 0 && pos != end; ++pos)
        --i;

    if (pos != end)
    {
        m_args.erase (pos);
        assert(idx < m_argv.size() - 1);
        m_argv.erase(m_argv.begin() + idx);
        if (idx < m_args_quote_char.size())
            m_args_quote_char.erase(m_args_quote_char.begin() + idx);
    }
}

void
Args::SetArguments (int argc, const char **argv)
{
    // m_argv will be rebuilt in UpdateArgvFromArgs() below, so there is
    // no need to clear it here.
    m_args.clear();
    m_args_quote_char.clear();

    // First copy each string
    for (size_t i=0; i<argc; ++i)
    {
        m_args.push_back (argv[i]);
        if ((argv[i][0] == '\'') || (argv[i][0] == '"') || (argv[i][0] == '`'))
            m_args_quote_char.push_back (argv[i][0]);
        else
            m_args_quote_char.push_back ('\0');
    }

    UpdateArgvFromArgs();
}

void
Args::SetArguments (const char **argv)
{
    // m_argv will be rebuilt in UpdateArgvFromArgs() below, so there is
    // no need to clear it here.
    m_args.clear();
    m_args_quote_char.clear();
    
    if (argv)
    {
        // First copy each string
        for (size_t i=0; argv[i]; ++i)
        {
            m_args.push_back (argv[i]);
            if ((argv[i][0] == '\'') || (argv[i][0] == '"') || (argv[i][0] == '`'))
                m_args_quote_char.push_back (argv[i][0]);
            else
                m_args_quote_char.push_back ('\0');
        }
    }
    
    UpdateArgvFromArgs();
}


Error
Args::ParseOptions (Options &options)
{
    StreamString sstr;
    Error error;
    struct option *long_options = options.GetLongOptions();
    if (long_options == NULL)
    {
        error.SetErrorStringWithFormat("invalid long options");
        return error;
    }

    for (int i=0; long_options[i].name != NULL; ++i)
    {
        if (long_options[i].flag == NULL)
        {
            if (isprint8(long_options[i].val))
            {
                sstr << (char)long_options[i].val;
                switch (long_options[i].has_arg)
                {
                default:
                case no_argument:                       break;
                case required_argument: sstr << ':';    break;
                case optional_argument: sstr << "::";   break;
                }
            }
        }
    }
#ifdef __GLIBC__
    optind = 0;
#else
    optreset = 1;
    optind = 1;
#endif
    int val;
    while (1)
    {
        int long_options_index = -1;
        val = ::getopt_long(GetArgumentCount(),
                            GetArgumentVector(),
                            sstr.GetData(),
                            long_options,
                            &long_options_index);
        if (val == -1)
            break;

        // Did we get an error?
        if (val == '?')
        {
            error.SetErrorStringWithFormat("unknown or ambiguous option");
            break;
        }
        // The option auto-set itself
        if (val == 0)
            continue;

        ((Options *) &options)->OptionSeen (val);

        // Lookup the long option index
        if (long_options_index == -1)
        {
            for (int i=0;
                 long_options[i].name || long_options[i].has_arg || long_options[i].flag || long_options[i].val;
                 ++i)
            {
                if (long_options[i].val == val)
                {
                    long_options_index = i;
                    break;
                }
            }
        }
        // Call the callback with the option
        if (long_options_index >= 0)
        {
            error = options.SetOptionValue(long_options_index,
                                           long_options[long_options_index].has_arg == no_argument ? NULL : optarg);
        }
        else
        {
            error.SetErrorStringWithFormat("invalid option with value '%i'", val);
        }
        if (error.Fail())
            break;
    }

    // Update our ARGV now that get options has consumed all the options
    m_argv.erase(m_argv.begin(), m_argv.begin() + optind);
    UpdateArgsAfterOptionParsing ();
    return error;
}

void
Args::Clear ()
{
    m_args.clear ();
    m_argv.clear ();
    m_args_quote_char.clear();
}

int32_t
Args::StringToSInt32 (const char *s, int32_t fail_value, int base, bool *success_ptr)
{
    if (s && s[0])
    {
        char *end = NULL;
        int32_t uval = ::strtol (s, &end, base);
        if (*end == '\0')
        {
            if (success_ptr) *success_ptr = true;
            return uval; // All characters were used, return the result
        }
    }
    if (success_ptr) *success_ptr = false;
    return fail_value;
}

uint32_t
Args::StringToUInt32 (const char *s, uint32_t fail_value, int base, bool *success_ptr)
{
    if (s && s[0])
    {
        char *end = NULL;
        uint32_t uval = ::strtoul (s, &end, base);
        if (*end == '\0')
        {
            if (success_ptr) *success_ptr = true;
            return uval; // All characters were used, return the result
        }
    }
    if (success_ptr) *success_ptr = false;
    return fail_value;
}


int64_t
Args::StringToSInt64 (const char *s, int64_t fail_value, int base, bool *success_ptr)
{
    if (s && s[0])
    {
        char *end = NULL;
        int64_t uval = ::strtoll (s, &end, base);
        if (*end == '\0')
        {
            if (success_ptr) *success_ptr = true;
            return uval; // All characters were used, return the result
        }
    }
    if (success_ptr) *success_ptr = false;
    return fail_value;
}

uint64_t
Args::StringToUInt64 (const char *s, uint64_t fail_value, int base, bool *success_ptr)
{
    if (s && s[0])
    {
        char *end = NULL;
        uint64_t uval = ::strtoull (s, &end, base);
        if (*end == '\0')
        {
            if (success_ptr) *success_ptr = true;
            return uval; // All characters were used, return the result
        }
    }
    if (success_ptr) *success_ptr = false;
    return fail_value;
}

lldb::addr_t
Args::StringToAddress (const char *s, lldb::addr_t fail_value, bool *success_ptr)
{
    if (s && s[0])
    {
        char *end = NULL;
        lldb::addr_t addr = ::strtoull (s, &end, 0);
        if (*end == '\0')
        {
            if (success_ptr) *success_ptr = true;
            return addr; // All characters were used, return the result
        }
        // Try base 16 with no prefix...
        addr = ::strtoull (s, &end, 16);
        if (*end == '\0')
        {
            if (success_ptr) *success_ptr = true;
            return addr; // All characters were used, return the result
        }
    }
    if (success_ptr) *success_ptr = false;
    return fail_value;
}

bool
Args::StringToBoolean (const char *s, bool fail_value, bool *success_ptr)
{
    if (s && s[0])
    {
        if (::strcasecmp (s, "false") == 0 ||
            ::strcasecmp (s, "off") == 0 ||
            ::strcasecmp (s, "no") == 0 ||
                ::strcmp (s, "0") == 0)
        {
            if (success_ptr)
                *success_ptr = true;
            return false;
        }
        else
        if (::strcasecmp (s, "true") == 0 ||
            ::strcasecmp (s, "on") == 0 ||
            ::strcasecmp (s, "yes") == 0 ||
                ::strcmp (s, "1") == 0)
        {
            if (success_ptr) *success_ptr = true;
            return true;
        }
    }
    if (success_ptr) *success_ptr = false;
    return fail_value;
}

const char *
Args::StringToVersion (const char *s, uint32_t &major, uint32_t &minor, uint32_t &update)
{
    major = UINT32_MAX;
    minor = UINT32_MAX;
    update = UINT32_MAX;

    if (s && s[0])
    {
        char *pos = NULL;
        uint32_t uval32;
        uval32 = ::strtoul (s, &pos, 0);
        if (pos == s)
            return s;
        major = uval32;
        if (*pos == '\0')
        {
            return pos;   // Decoded major and got end of string
        }
        else if (*pos == '.')
        {
            const char *minor_cstr = pos + 1;
            uval32 = ::strtoul (minor_cstr, &pos, 0);
            if (pos == minor_cstr)
                return pos; // Didn't get any digits for the minor version...
            minor = uval32;
            if (*pos == '.')
            {
                const char *update_cstr = pos + 1;
                uval32 = ::strtoul (update_cstr, &pos, 0);
                if (pos == update_cstr)
                    return pos;
                update = uval32;
            }
            return pos;
        }
    }
    return 0;
}

const char *
Args::GetShellSafeArgument (const char *unsafe_arg, std::string &safe_arg)
{
    safe_arg.assign (unsafe_arg);
    size_t prev_pos = 0;
    while (prev_pos < safe_arg.size())
    {
        // Escape spaces and quotes
        size_t pos = safe_arg.find_first_of(" '\"", prev_pos);
        if (pos != std::string::npos)
        {
            safe_arg.insert (pos, 1, '\\');
            prev_pos = pos + 2;
        }
        else
            break;
    }
    return safe_arg.c_str();
}


int32_t
Args::StringToOptionEnum (const char *s, OptionEnumValueElement *enum_values, int32_t fail_value, Error &error)
{    
    if (enum_values)
    {
        if (s && s[0])
        {
            for (int i = 0; enum_values[i].string_value != NULL ; i++) 
            {
                if (strstr(enum_values[i].string_value, s) == enum_values[i].string_value)
                {
                    error.Clear();
                    return enum_values[i].value;
                }
            }
        }

        StreamString strm;
        strm.PutCString ("invalid enumeration value, valid values are: ");
        for (int i = 0; enum_values[i].string_value != NULL; i++) 
        {
            strm.Printf ("%s\"%s\"", 
                         i > 0 ? ", " : "",
                         enum_values[i].string_value);
        }
        error.SetErrorString(strm.GetData());
    }
    else
    {
        error.SetErrorString ("invalid enumeration argument");
    }
    return fail_value;
}

ScriptLanguage
Args::StringToScriptLanguage (const char *s, ScriptLanguage fail_value, bool *success_ptr)
{
    if (s && s[0])
    {
        if ((::strcasecmp (s, "python") == 0) ||
            (::strcasecmp (s, "default") == 0 && eScriptLanguagePython == eScriptLanguageDefault))
        {
            if (success_ptr) *success_ptr = true;
            return eScriptLanguagePython;
        }
        if (::strcasecmp (s, "none"))
        {
            if (success_ptr) *success_ptr = true;
            return eScriptLanguageNone;
        }
    }
    if (success_ptr) *success_ptr = false;
    return fail_value;
}

Error
Args::StringToFormat
(
    const char *s,
    lldb::Format &format,
    uint32_t *byte_size_ptr
)
{
    format = eFormatInvalid;
    Error error;

    if (s && s[0])
    {
        if (byte_size_ptr)
        {
            if (isdigit (s[0]))
            {
                char *format_char = NULL;
                unsigned long byte_size = ::strtoul (s, &format_char, 0);
                if (byte_size != ULONG_MAX)
                    *byte_size_ptr = byte_size;
                s = format_char;
            }
            else
                *byte_size_ptr = 0;
        }

        const bool partial_match_ok = true;
        if (!FormatManager::GetFormatFromCString (s, partial_match_ok, format))
        {
            StreamString error_strm;
            error_strm.Printf ("Invalid format character or name '%s'. Valid values are:\n", s);
            for (Format f = eFormatDefault; f < kNumFormats; f = Format(f+1))
            {
                char format_char = FormatManager::GetFormatAsFormatChar(f);
                if (format_char)
                    error_strm.Printf ("'%c' or ", format_char);
            
                error_strm.Printf ("\"%s\"", FormatManager::GetFormatAsCString(f));
                error_strm.EOL();
            }
            
            if (byte_size_ptr)
                error_strm.PutCString ("An optional byte size can precede the format character.\n");
            error.SetErrorString(error_strm.GetString().c_str());
        }

        if (error.Fail())
            return error;
    }
    else
    {
        error.SetErrorStringWithFormat("%s option string", s ? "empty" : "invalid");
    }
    return error;
}

lldb::Encoding
Args::StringToEncoding (const char *s, lldb::Encoding fail_value)
{
    if (s && s[0])
    {
        if (strcmp(s, "uint") == 0)
            return eEncodingUint;
        else if (strcmp(s, "sint") == 0)
            return eEncodingSint;
        else if (strcmp(s, "ieee754") == 0)
            return eEncodingIEEE754;
        else if (strcmp(s, "vector") == 0)
            return eEncodingVector;
    }
    return fail_value;
}

uint32_t
Args::StringToGenericRegister (const char *s)
{
    if (s && s[0])
    {
        if (strcmp(s, "pc") == 0)
            return LLDB_REGNUM_GENERIC_PC;
        else if (strcmp(s, "sp") == 0)
            return LLDB_REGNUM_GENERIC_SP;
        else if (strcmp(s, "fp") == 0)
            return LLDB_REGNUM_GENERIC_FP;
        else if (strcmp(s, "ra") == 0)
            return LLDB_REGNUM_GENERIC_RA;
        else if (strcmp(s, "flags") == 0)
            return LLDB_REGNUM_GENERIC_FLAGS;
        else if (strncmp(s, "arg", 3) == 0)
        {
            if (s[3] && s[4] == '\0')
            {
                switch (s[3])
                {
                    case '1': return LLDB_REGNUM_GENERIC_ARG1;
                    case '2': return LLDB_REGNUM_GENERIC_ARG2;
                    case '3': return LLDB_REGNUM_GENERIC_ARG3;
                    case '4': return LLDB_REGNUM_GENERIC_ARG4;
                    case '5': return LLDB_REGNUM_GENERIC_ARG5;
                    case '6': return LLDB_REGNUM_GENERIC_ARG6;
                    case '7': return LLDB_REGNUM_GENERIC_ARG7;
                    case '8': return LLDB_REGNUM_GENERIC_ARG8;
                }
            }
        }
    }
    return LLDB_INVALID_REGNUM;
}


void
Args::LongestCommonPrefix (std::string &common_prefix)
{
    arg_sstr_collection::iterator pos, end = m_args.end();
    pos = m_args.begin();
    if (pos == end)
        common_prefix.clear();
    else
        common_prefix = (*pos);

    for (++pos; pos != end; ++pos)
    {
        size_t new_size = (*pos).size();

        // First trim common_prefix if it is longer than the current element:
        if (common_prefix.size() > new_size)
            common_prefix.erase (new_size);

        // Then trim it at the first disparity:

        for (size_t i = 0; i < common_prefix.size(); i++)
        {
            if ((*pos)[i]  != common_prefix[i])
            {
                common_prefix.erase(i);
                break;
            }
        }

        // If we've emptied the common prefix, we're done.
        if (common_prefix.empty())
            break;
    }
}

size_t
Args::FindArgumentIndexForOption (struct option *long_options, int long_options_index)
{
    char short_buffer[3];
    char long_buffer[255];
    ::snprintf (short_buffer, sizeof (short_buffer), "-%c", long_options[long_options_index].val);
    ::snprintf (long_buffer, sizeof (long_buffer),  "--%s", long_options[long_options_index].name);
    size_t end = GetArgumentCount ();
    size_t idx = 0;
    while (idx < end)
    {   
        if ((::strncmp (GetArgumentAtIndex (idx), short_buffer, strlen (short_buffer)) == 0)
            || (::strncmp (GetArgumentAtIndex (idx), long_buffer, strlen (long_buffer)) == 0))
            {
                return idx;
            }
        ++idx;
    }

    return end;
}

bool
Args::IsPositionalArgument (const char *arg)
{
    if (arg == NULL)
        return false;
        
    bool is_positional = true;
    char *cptr = (char *) arg;
    
    if (cptr[0] == '%')
    {
        ++cptr;
        while (isdigit (cptr[0]))
            ++cptr;
        if (cptr[0] != '\0')
            is_positional = false;
    }
    else
        is_positional = false;

    return is_positional;
}

void
Args::ParseAliasOptions (Options &options,
                         CommandReturnObject &result,
                         OptionArgVector *option_arg_vector,
                         std::string &raw_input_string)
{
    StreamString sstr;
    int i;
    struct option *long_options = options.GetLongOptions();

    if (long_options == NULL)
    {
        result.AppendError ("invalid long options");
        result.SetStatus (eReturnStatusFailed);
        return;
    }

    for (i = 0; long_options[i].name != NULL; ++i)
    {
        if (long_options[i].flag == NULL)
        {
            sstr << (char) long_options[i].val;
            switch (long_options[i].has_arg)
            {
                default:
                case no_argument:
                    break;
                case required_argument:
                    sstr << ":";
                    break;
                case optional_argument:
                    sstr << "::";
                    break;
            }
        }
    }

#ifdef __GLIBC__
    optind = 0;
#else
    optreset = 1;
    optind = 1;
#endif
    int val;
    while (1)
    {
        int long_options_index = -1;
        val = ::getopt_long (GetArgumentCount(), GetArgumentVector(), sstr.GetData(), long_options,
                             &long_options_index);

        if (val == -1)
            break;

        if (val == '?')
        {
            result.AppendError ("unknown or ambiguous option");
            result.SetStatus (eReturnStatusFailed);
            break;
        }

        if (val == 0)
            continue;

        ((Options *) &options)->OptionSeen (val);

        // Look up the long option index
        if (long_options_index == -1)
        {
            for (int j = 0;
                 long_options[j].name || long_options[j].has_arg || long_options[j].flag || long_options[j].val;
                 ++j)
            {
                if (long_options[j].val == val)
                {
                    long_options_index = j;
                    break;
                }
            }
        }

        // See if the option takes an argument, and see if one was supplied.
        if (long_options_index >= 0)
        {
            StreamString option_str;
            option_str.Printf ("-%c", val);

            switch (long_options[long_options_index].has_arg)
            {
            case no_argument:
                option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), 
                                                             OptionArgValue (no_argument, "<no-argument>")));
                result.SetStatus (eReturnStatusSuccessFinishNoResult);
                break;
            case required_argument:
                if (optarg != NULL)
                {
                    option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()),
                                                                 OptionArgValue (required_argument, 
                                                                                 std::string (optarg))));
                    result.SetStatus (eReturnStatusSuccessFinishNoResult);
                }
                else
                {
                    result.AppendErrorWithFormat ("Option '%s' is missing argument specifier.\n",
                                                 option_str.GetData());
                    result.SetStatus (eReturnStatusFailed);
                }
                break;
            case optional_argument:
                if (optarg != NULL)
                {
                    option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()),
                                                                 OptionArgValue (optional_argument, 
                                                                                 std::string (optarg))));
                    result.SetStatus (eReturnStatusSuccessFinishNoResult);
                }
                else
                {
                    option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()),
                                                                 OptionArgValue (optional_argument, "<no-argument>")));
                    result.SetStatus (eReturnStatusSuccessFinishNoResult);
                }
                break;
            default:
                result.AppendErrorWithFormat ("error with options table; invalid value in has_arg field for option '%c'.\n", val);
                result.SetStatus (eReturnStatusFailed);
                break;
            }
        }
        else
        {
            result.AppendErrorWithFormat ("Invalid option with value '%c'.\n", val);
            result.SetStatus (eReturnStatusFailed);
        }

        if (long_options_index >= 0)
        {
            // Find option in the argument list; also see if it was supposed to take an argument and if one was
            // supplied.  Remove option (and argument, if given) from the argument list.  Also remove them from
            // the raw_input_string, if one was passed in.
            size_t idx = FindArgumentIndexForOption (long_options, long_options_index);
            if (idx < GetArgumentCount())
            {
                if (raw_input_string.size() > 0)
                {
                    const char *tmp_arg = GetArgumentAtIndex (idx);
                    size_t pos = raw_input_string.find (tmp_arg);
                    if (pos != std::string::npos)
                        raw_input_string.erase (pos, strlen (tmp_arg));
                }
                ReplaceArgumentAtIndex (idx, "");
                if ((long_options[long_options_index].has_arg != no_argument)
                    && (optarg != NULL)
                    && (idx+1 < GetArgumentCount())
                    && (strcmp (optarg, GetArgumentAtIndex(idx+1)) == 0))
                {
                    if (raw_input_string.size() > 0)
                    {
                        const char *tmp_arg = GetArgumentAtIndex (idx+1);
                        size_t pos = raw_input_string.find (tmp_arg);
                        if (pos != std::string::npos)
                            raw_input_string.erase (pos, strlen (tmp_arg));
                    }
                    ReplaceArgumentAtIndex (idx+1, "");
                }
            }
        }

        if (!result.Succeeded())
            break;
    }
}

void
Args::ParseArgsForCompletion
(
    Options &options,
    OptionElementVector &option_element_vector,
    uint32_t cursor_index
)
{
    StreamString sstr;
    struct option *long_options = options.GetLongOptions();
    option_element_vector.clear();

    if (long_options == NULL)
    {
        return;
    }

    // Leading : tells getopt to return a : for a missing option argument AND
    // to suppress error messages.

    sstr << ":";
    for (int i = 0; long_options[i].name != NULL; ++i)
    {
        if (long_options[i].flag == NULL)
        {
            sstr << (char) long_options[i].val;
            switch (long_options[i].has_arg)
            {
                default:
                case no_argument:
                    break;
                case required_argument:
                    sstr << ":";
                    break;
                case optional_argument:
                    sstr << "::";
                    break;
            }
        }
    }

#ifdef __GLIBC__
    optind = 0;
#else
    optreset = 1;
    optind = 1;
#endif
    opterr = 0;

    int val;
    const OptionDefinition *opt_defs = options.GetDefinitions();

    // Fooey... getopt_long permutes the GetArgumentVector to move the options to the front.
    // So we have to build another Arg and pass that to getopt_long so it doesn't
    // change the one we have.

    std::vector<const char *> dummy_vec (GetArgumentVector(), GetArgumentVector() + GetArgumentCount() + 1);

    bool failed_once = false;
    uint32_t dash_dash_pos = -1;
        
    while (1)
    {
        bool missing_argument = false;
        int parse_start = optind;
        int long_options_index = -1;
        
        val = ::getopt_long (dummy_vec.size() - 1,
                             (char *const *) &dummy_vec.front(), 
                             sstr.GetData(), 
                             long_options,
                             &long_options_index);

        if (val == -1)
        {
            // When we're completing a "--" which is the last option on line, 
            if (failed_once)
                break;
                
            failed_once = true;
            
            // If this is a bare  "--" we mark it as such so we can complete it successfully later.
            // Handling the "--" is a little tricky, since that may mean end of options or arguments, or the
            // user might want to complete options by long name.  I make this work by checking whether the
            // cursor is in the "--" argument, and if so I assume we're completing the long option, otherwise
            // I let it pass to getopt_long which will terminate the option parsing.
            // Note, in either case we continue parsing the line so we can figure out what other options
            // were passed.  This will be useful when we come to restricting completions based on what other
            // options we've seen on the line.

            if (optind < dummy_vec.size() - 1 
                && (strcmp (dummy_vec[optind-1], "--") == 0))
            {
                dash_dash_pos = optind - 1;
                if (optind - 1 == cursor_index)
                {
                    option_element_vector.push_back (OptionArgElement (OptionArgElement::eBareDoubleDash, optind - 1, 
                                                                   OptionArgElement::eBareDoubleDash));
                    continue;
                }
                else
                    break;
            }
            else
                break;
        }
        else if (val == '?')
        {
            option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, optind - 1, 
                                                               OptionArgElement::eUnrecognizedArg));
            continue;
        }
        else if (val == 0)
        {
            continue;
        }
        else if (val == ':')
        {
            // This is a missing argument.
            val = optopt;
            missing_argument = true;
        }

        ((Options *) &options)->OptionSeen (val);

        // Look up the long option index
        if (long_options_index == -1)
        {
            for (int j = 0;
                 long_options[j].name || long_options[j].has_arg || long_options[j].flag || long_options[j].val;
                 ++j)
            {
                if (long_options[j].val == val)
                {
                    long_options_index = j;
                    break;
                }
            }
        }

        // See if the option takes an argument, and see if one was supplied.
        if (long_options_index >= 0)
        {
            int opt_defs_index = -1;
            for (int i = 0; ; i++)
            {
                if (opt_defs[i].short_option == 0)
                    break;
                else if (opt_defs[i].short_option == val)
                {
                    opt_defs_index = i;
                    break;
                }
            }

            switch (long_options[long_options_index].has_arg)
            {
            case no_argument:
                option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, 0));
                break;
            case required_argument:
                if (optarg != NULL)
                {
                    int arg_index;
                    if (missing_argument)
                        arg_index = -1;
                    else
                        arg_index = optind - 1;

                    option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 2, arg_index));
                }
                else
                {
                    option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 1, -1));
                }
                break;
            case optional_argument:
                if (optarg != NULL)
                {
                    option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 2, optind - 1));
                }
                else
                {
                    option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 2, optind - 1));
                }
                break;
            default:
                // The options table is messed up.  Here we'll just continue
                option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, optind - 1, 
                                                                   OptionArgElement::eUnrecognizedArg));
                break;
            }
        }
        else
        {
            option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, optind - 1, 
                                                               OptionArgElement::eUnrecognizedArg));
        }
    }
    
    // Finally we have to handle the case where the cursor index points at a single "-".  We want to mark that in
    // the option_element_vector, but only if it is not after the "--".  But it turns out that getopt_long just ignores
    // an isolated "-".  So we have to look it up by hand here.  We only care if it is AT the cursor position.
    
    if ((dash_dash_pos == -1 || cursor_index < dash_dash_pos)
         && strcmp (GetArgumentAtIndex(cursor_index), "-") == 0)
    {
        option_element_vector.push_back (OptionArgElement (OptionArgElement::eBareDash, cursor_index, 
                                                           OptionArgElement::eBareDash));
        
    }
}

void
Args::EncodeEscapeSequences (const char *src, std::string &dst)
{
    dst.clear();
    if (src)
    {
        for (const char *p = src; *p != '\0'; ++p)
        {
            size_t non_special_chars = ::strcspn (p, "\\");
            if (non_special_chars > 0)
            {
                dst.append(p, non_special_chars);
                p += non_special_chars;
                if (*p == '\0')
                    break;
            }
            
            if (*p == '\\')
            {
                ++p; // skip the slash
                switch (*p)
                {
                    case 'a' : dst.append(1, '\a'); break;
                    case 'b' : dst.append(1, '\b'); break;
                    case 'f' : dst.append(1, '\f'); break;
                    case 'n' : dst.append(1, '\n'); break;
                    case 'r' : dst.append(1, '\r'); break;
                    case 't' : dst.append(1, '\t'); break;
                    case 'v' : dst.append(1, '\v'); break;
                    case '\\': dst.append(1, '\\'); break;
                    case '\'': dst.append(1, '\''); break;
                    case '"' : dst.append(1, '"'); break;
                    case '0' :
                        // 1 to 3 octal chars
                    {
                        // Make a string that can hold onto the initial zero char,
                        // up to 3 octal digits, and a terminating NULL.
                        char oct_str[5] = { '\0', '\0', '\0', '\0', '\0' };
                        
                        int i;
                        for (i=0; (p[i] >= '0' && p[i] <= '7') && i<4; ++i)
                            oct_str[i] = p[i];
                        
                        // We don't want to consume the last octal character since
                        // the main for loop will do this for us, so we advance p by
                        // one less than i (even if i is zero)
                        p += i - 1;
                        unsigned long octal_value = ::strtoul (oct_str, NULL, 8);
                        if (octal_value <= UINT8_MAX)
                        {
                            const char octal_char = octal_value;
                            dst.append(1, octal_char);
                        }
                    }
                        break;
                        
                    case 'x':
                        // hex number in the format
                        if (isxdigit(p[1]))
                        {
                            ++p;    // Skip the 'x'
                            
                            // Make a string that can hold onto two hex chars plus a
                            // NULL terminator
                            char hex_str[3] = { *p, '\0', '\0' };
                            if (isxdigit(p[1]))
                            {
                                ++p; // Skip the first of the two hex chars
                                hex_str[1] = *p;
                            }
                            
                            unsigned long hex_value = strtoul (hex_str, NULL, 16);
                            if (hex_value <= UINT8_MAX)
                                dst.append (1, (char)hex_value);
                        }
                        else
                        {
                            dst.append(1, 'x');
                        }
                        break;
                        
                    default:
                        // Just desensitize any other character by just printing what
                        // came after the '\'
                        dst.append(1, *p);
                        break;
                        
                }
            }
        }
    }
}


void
Args::ExpandEscapedCharacters (const char *src, std::string &dst)
{
    dst.clear();
    if (src)
    {
        for (const char *p = src; *p != '\0'; ++p)
        {
            if (isprint8(*p))
                dst.append(1, *p);
            else
            {
                switch (*p)
                {
                    case '\a': dst.append("\\a"); break;
                    case '\b': dst.append("\\b"); break;
                    case '\f': dst.append("\\f"); break;
                    case '\n': dst.append("\\n"); break;
                    case '\r': dst.append("\\r"); break;
                    case '\t': dst.append("\\t"); break;
                    case '\v': dst.append("\\v"); break;
                    case '\'': dst.append("\\'"); break;
                    case '"': dst.append("\\\""); break;
                    case '\\': dst.append("\\\\"); break;
                    default:
                        {
                            // Just encode as octal
                            dst.append("\\0");
                            char octal_str[32];
                            snprintf(octal_str, sizeof(octal_str), "%o", *p);
                            dst.append(octal_str);
                        }
                        break;
                }
            }
        }
    }
}