Index: lldb/trunk/include/lldb/Interpreter/Args.h =================================================================== --- lldb/trunk/include/lldb/Interpreter/Args.h +++ lldb/trunk/include/lldb/Interpreter/Args.h @@ -1,368 +0,0 @@ -//===-- Args.h --------------------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef liblldb_Command_h_ -#define liblldb_Command_h_ - -// C Includes -// C++ Includes -#include -#include -#include - -// Other libraries and framework includes -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringRef.h" -// Project includes -#include "lldb/Utility/Environment.h" -#include "lldb/lldb-private-types.h" -#include "lldb/lldb-types.h" - -namespace lldb_private { - -//---------------------------------------------------------------------- -/// @class Args Args.h "lldb/Interpreter/Args.h" -/// @brief A command line argument class. -/// -/// The Args class is designed to be fed a command line. The -/// command line is copied into an internal buffer and then split up -/// into arguments. Arguments are space delimited if there are no quotes -/// (single, double, or backtick quotes) surrounding the argument. Spaces -/// can be escaped using a \ character to avoid having to surround an -/// argument that contains a space with quotes. -//---------------------------------------------------------------------- -class Args { -public: - struct ArgEntry { - private: - friend class Args; - std::unique_ptr ptr; - - char *data() { return ptr.get(); } - - public: - ArgEntry() = default; - ArgEntry(llvm::StringRef str, char quote); - - llvm::StringRef ref; - char quote; - const char *c_str() const { return ptr.get(); } - }; - - //------------------------------------------------------------------ - /// Construct with an option command string. - /// - /// @param[in] command - /// A NULL terminated command that will be copied and split up - /// into arguments. - /// - /// @see Args::SetCommandString(llvm::StringRef) - //------------------------------------------------------------------ - Args(llvm::StringRef command = llvm::StringRef()); - - Args(const Args &rhs); - explicit Args(const StringList &list); - - Args &operator=(const Args &rhs); - - //------------------------------------------------------------------ - /// Destructor. - //------------------------------------------------------------------ - ~Args(); - - explicit Args(const Environment &env) : Args() { - SetArguments(const_cast(env.getEnvp().get())); - } - - explicit operator Environment() const { return GetConstArgumentVector(); } - - //------------------------------------------------------------------ - /// Dump all entries to the stream \a s using label \a label_name. - /// - /// If label_name is nullptr, the dump operation is skipped. - /// - /// @param[in] s - /// The stream to which to dump all arguments in the argument - /// vector. - /// @param[in] label_name - /// The label_name to use as the label printed for each - /// entry of the args like so: - /// {label_name}[{index}]={value} - //------------------------------------------------------------------ - void Dump(Stream &s, const char *label_name = "argv") const; - - //------------------------------------------------------------------ - /// Sets the command string contained by this object. - /// - /// The command string will be copied and split up into arguments - /// that can be accessed via the accessor functions. - /// - /// @param[in] command - /// A command StringRef that will be copied and split up - /// into arguments. - /// - /// @see Args::GetArgumentCount() const - /// @see Args::GetArgumentAtIndex (size_t) const - /// @see Args::GetArgumentVector () - /// @see Args::Shift () - /// @see Args::Unshift (const char *) - //------------------------------------------------------------------ - void SetCommandString(llvm::StringRef command); - - bool GetCommandString(std::string &command) const; - - bool GetQuotedCommandString(std::string &command) const; - - //------------------------------------------------------------------ - /// Gets the number of arguments left in this command object. - /// - /// @return - /// The number or arguments in this object. - //------------------------------------------------------------------ - size_t GetArgumentCount() const; - bool empty() const { return GetArgumentCount() == 0; } - - //------------------------------------------------------------------ - /// Gets the NULL terminated C string argument pointer for the - /// argument at index \a idx. - /// - /// @return - /// The NULL terminated C string argument pointer if \a idx is a - /// valid argument index, NULL otherwise. - //------------------------------------------------------------------ - const char *GetArgumentAtIndex(size_t idx) const; - - llvm::ArrayRef entries() const { return m_entries; } - char GetArgumentQuoteCharAtIndex(size_t idx) const; - - using const_iterator = std::vector::const_iterator; - - const_iterator begin() const { return m_entries.begin(); } - const_iterator end() const { return m_entries.end(); } - - size_t size() const { return GetArgumentCount(); } - const ArgEntry &operator[](size_t n) const { return m_entries[n]; } - - //------------------------------------------------------------------ - /// Gets the argument vector. - /// - /// The value returned by this function can be used by any function - /// that takes and vector. The return value is just like \a argv - /// in the standard C entry point function: - /// \code - /// int main (int argc, const char **argv); - /// \endcode - /// - /// @return - /// An array of NULL terminated C string argument pointers that - /// also has a terminating NULL C string pointer - //------------------------------------------------------------------ - char **GetArgumentVector(); - - //------------------------------------------------------------------ - /// Gets the argument vector. - /// - /// The value returned by this function can be used by any function - /// that takes and vector. The return value is just like \a argv - /// in the standard C entry point function: - /// \code - /// int main (int argc, const char **argv); - /// \endcode - /// - /// @return - /// An array of NULL terminate C string argument pointers that - /// also has a terminating NULL C string pointer - //------------------------------------------------------------------ - const char **GetConstArgumentVector() const; - - //------------------------------------------------------------------ - /// Gets the argument as an ArrayRef. Note that the return value does *not* - /// have a nullptr const char * at the end, as the size of the list is - /// embedded in the ArrayRef object. - //------------------------------------------------------------------ - llvm::ArrayRef GetArgumentArrayRef() const { - return llvm::makeArrayRef(m_argv).drop_back(); - } - - //------------------------------------------------------------------ - /// Appends a new argument to the end of the list argument list. - /// - /// @param[in] arg_cstr - /// The new argument as a NULL terminated C string. - /// - /// @param[in] quote_char - /// If the argument was originally quoted, put in the quote char here. - //------------------------------------------------------------------ - void AppendArgument(llvm::StringRef arg_str, char quote_char = '\0'); - - void AppendArguments(const Args &rhs); - - void AppendArguments(const char **argv); - - //------------------------------------------------------------------ - /// Insert the argument value at index \a idx to \a arg_cstr. - /// - /// @param[in] idx - /// The index of where to insert the argument. - /// - /// @param[in] arg_cstr - /// The new argument as a NULL terminated C string. - /// - /// @param[in] quote_char - /// If the argument was originally quoted, put in the quote char here. - /// - /// @return - /// The NULL terminated C string of the copy of \a arg_cstr. - //------------------------------------------------------------------ - void InsertArgumentAtIndex(size_t idx, llvm::StringRef arg_str, - char quote_char = '\0'); - - //------------------------------------------------------------------ - /// Replaces the argument value at index \a idx to \a arg_cstr - /// if \a idx is a valid argument index. - /// - /// @param[in] idx - /// The index of the argument that will have its value replaced. - /// - /// @param[in] arg_cstr - /// The new argument as a NULL terminated C string. - /// - /// @param[in] quote_char - /// If the argument was originally quoted, put in the quote char here. - //------------------------------------------------------------------ - void ReplaceArgumentAtIndex(size_t idx, llvm::StringRef arg_str, - char quote_char = '\0'); - - //------------------------------------------------------------------ - /// Deletes the argument value at index - /// if \a idx is a valid argument index. - /// - /// @param[in] idx - /// The index of the argument that will have its value replaced. - /// - //------------------------------------------------------------------ - void DeleteArgumentAtIndex(size_t idx); - - //------------------------------------------------------------------ - /// Sets the argument vector value, optionally copying all - /// arguments into an internal buffer. - /// - /// Sets the arguments to match those found in \a argv. All argument - /// strings will be copied into an internal buffers. - // - // FIXME: Handle the quote character somehow. - //------------------------------------------------------------------ - void SetArguments(size_t argc, const char **argv); - - void SetArguments(const char **argv); - - //------------------------------------------------------------------ - /// Shifts the first argument C string value of the array off the - /// argument array. - /// - /// The string value will be freed, so a copy of the string should - /// be made by calling Args::GetArgumentAtIndex (size_t) const - /// first and copying the returned value before calling - /// Args::Shift(). - /// - /// @see Args::GetArgumentAtIndex (size_t) const - //------------------------------------------------------------------ - void Shift(); - - //------------------------------------------------------------------ - /// Inserts a class owned copy of \a arg_cstr at the beginning of - /// the argument vector. - /// - /// A copy \a arg_cstr will be made. - /// - /// @param[in] arg_cstr - /// The argument to push on the front of the argument stack. - /// - /// @param[in] quote_char - /// If the argument was originally quoted, put in the quote char here. - //------------------------------------------------------------------ - void Unshift(llvm::StringRef arg_str, char quote_char = '\0'); - - //------------------------------------------------------------------ - // Clear the arguments. - // - // For re-setting or blanking out the list of arguments. - //------------------------------------------------------------------ - void Clear(); - - static const char *StripSpaces(std::string &s, bool leading = true, - bool trailing = true, - bool return_null_if_empty = true); - - static bool UInt64ValueIsValidForByteSize(uint64_t uval64, - size_t total_byte_size) { - if (total_byte_size > 8) - return false; - - if (total_byte_size == 8) - return true; - - const uint64_t max = ((uint64_t)1 << (uint64_t)(total_byte_size * 8)) - 1; - return uval64 <= max; - } - - static bool SInt64ValueIsValidForByteSize(int64_t sval64, - size_t total_byte_size) { - if (total_byte_size > 8) - return false; - - if (total_byte_size == 8) - return true; - - const int64_t max = ((int64_t)1 << (uint64_t)(total_byte_size * 8 - 1)) - 1; - const int64_t min = ~(max); - return min <= sval64 && sval64 <= max; - } - - static lldb::Encoding - StringToEncoding(llvm::StringRef s, - lldb::Encoding fail_value = lldb::eEncodingInvalid); - - static uint32_t StringToGenericRegister(llvm::StringRef s); - - static bool StringToVersion(llvm::StringRef string, uint32_t &major, - uint32_t &minor, uint32_t &update); - - static const char *GetShellSafeArgument(const FileSpec &shell, - const char *unsafe_arg, - std::string &safe_arg); - - // EncodeEscapeSequences will change the textual representation of common - // escape sequences like "\n" (two characters) into a single '\n'. It does - // this for all of the supported escaped sequences and for the \0ooo (octal) - // and \xXX (hex). The resulting "dst" string will contain the character - // versions of all supported escape sequences. The common supported escape - // sequences are: "\a", "\b", "\f", "\n", "\r", "\t", "\v", "\'", "\"", "\\". - - static void EncodeEscapeSequences(const char *src, std::string &dst); - - // ExpandEscapeSequences will change a string of possibly non-printable - // characters and expand them into text. So '\n' will turn into two characters - // like "\n" which is suitable for human reading. When a character is not - // printable and isn't one of the common in escape sequences listed in the - // help for EncodeEscapeSequences, then it will be encoded as octal. Printable - // characters are left alone. - static void ExpandEscapedCharacters(const char *src, std::string &dst); - - static std::string EscapeLLDBCommandArgument(const std::string &arg, - char quote_char); - -private: - std::vector m_entries; - std::vector m_argv; -}; - -} // namespace lldb_private - -#endif // liblldb_Command_h_ Index: lldb/trunk/include/lldb/Interpreter/CommandAlias.h =================================================================== --- lldb/trunk/include/lldb/Interpreter/CommandAlias.h +++ lldb/trunk/include/lldb/Interpreter/CommandAlias.h @@ -16,8 +16,8 @@ // Other libraries and framework includes // Project includes -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandObject.h" +#include "lldb/Utility/Args.h" #include "lldb/lldb-forward.h" namespace lldb_private { Index: lldb/trunk/include/lldb/Interpreter/CommandInterpreter.h =================================================================== --- lldb/trunk/include/lldb/Interpreter/CommandInterpreter.h +++ lldb/trunk/include/lldb/Interpreter/CommandInterpreter.h @@ -19,11 +19,11 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/Event.h" #include "lldb/Core/IOHandler.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandAlias.h" #include "lldb/Interpreter/CommandHistory.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StringList.h" #include "lldb/lldb-forward.h" Index: lldb/trunk/include/lldb/Interpreter/CommandObject.h =================================================================== --- lldb/trunk/include/lldb/Interpreter/CommandObject.h +++ lldb/trunk/include/lldb/Interpreter/CommandObject.h @@ -20,10 +20,10 @@ // Project includes #include "lldb/Utility/Flags.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/Options.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/StringList.h" #include "lldb/lldb-private.h" Index: lldb/trunk/include/lldb/Interpreter/Options.h =================================================================== --- lldb/trunk/include/lldb/Interpreter/Options.h +++ lldb/trunk/include/lldb/Interpreter/Options.h @@ -17,7 +17,7 @@ // Other libraries and framework includes // Project includes -#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Status.h" #include "lldb/lldb-defines.h" #include "lldb/lldb-private.h" Index: lldb/trunk/include/lldb/Target/ProcessInfo.h =================================================================== --- lldb/trunk/include/lldb/Target/ProcessInfo.h +++ lldb/trunk/include/lldb/Target/ProcessInfo.h @@ -11,8 +11,8 @@ #define liblldb_ProcessInfo_h_ // LLDB headers -#include "lldb/Interpreter/Args.h" #include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Environment.h" #include "lldb/Utility/FileSpec.h" Index: lldb/trunk/include/lldb/Utility/Args.h =================================================================== --- lldb/trunk/include/lldb/Utility/Args.h +++ lldb/trunk/include/lldb/Utility/Args.h @@ -0,0 +1,363 @@ +//===-- Args.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_UTILITY_ARGS_H +#define LLDB_UTILITY_ARGS_H + +#include "lldb/Utility/Environment.h" +#include "lldb/lldb-private-types.h" +#include "lldb/lldb-types.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include +#include +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Args Args.h "lldb/Utility/Args.h" +/// @brief A command line argument class. +/// +/// The Args class is designed to be fed a command line. The +/// command line is copied into an internal buffer and then split up +/// into arguments. Arguments are space delimited if there are no quotes +/// (single, double, or backtick quotes) surrounding the argument. Spaces +/// can be escaped using a \ character to avoid having to surround an +/// argument that contains a space with quotes. +//---------------------------------------------------------------------- +class Args { +public: + struct ArgEntry { + private: + friend class Args; + std::unique_ptr ptr; + + char *data() { return ptr.get(); } + + public: + ArgEntry() = default; + ArgEntry(llvm::StringRef str, char quote); + + llvm::StringRef ref; + char quote; + const char *c_str() const { return ptr.get(); } + }; + + //------------------------------------------------------------------ + /// Construct with an option command string. + /// + /// @param[in] command + /// A NULL terminated command that will be copied and split up + /// into arguments. + /// + /// @see Args::SetCommandString(llvm::StringRef) + //------------------------------------------------------------------ + Args(llvm::StringRef command = llvm::StringRef()); + + Args(const Args &rhs); + explicit Args(const StringList &list); + + Args &operator=(const Args &rhs); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~Args(); + + explicit Args(const Environment &env) : Args() { + SetArguments(const_cast(env.getEnvp().get())); + } + + explicit operator Environment() const { return GetConstArgumentVector(); } + + //------------------------------------------------------------------ + /// Dump all entries to the stream \a s using label \a label_name. + /// + /// If label_name is nullptr, the dump operation is skipped. + /// + /// @param[in] s + /// The stream to which to dump all arguments in the argument + /// vector. + /// @param[in] label_name + /// The label_name to use as the label printed for each + /// entry of the args like so: + /// {label_name}[{index}]={value} + //------------------------------------------------------------------ + void Dump(Stream &s, const char *label_name = "argv") const; + + //------------------------------------------------------------------ + /// Sets the command string contained by this object. + /// + /// The command string will be copied and split up into arguments + /// that can be accessed via the accessor functions. + /// + /// @param[in] command + /// A command StringRef that will be copied and split up + /// into arguments. + /// + /// @see Args::GetArgumentCount() const + /// @see Args::GetArgumentAtIndex (size_t) const + /// @see Args::GetArgumentVector () + /// @see Args::Shift () + /// @see Args::Unshift (const char *) + //------------------------------------------------------------------ + void SetCommandString(llvm::StringRef command); + + bool GetCommandString(std::string &command) const; + + bool GetQuotedCommandString(std::string &command) const; + + //------------------------------------------------------------------ + /// Gets the number of arguments left in this command object. + /// + /// @return + /// The number or arguments in this object. + //------------------------------------------------------------------ + size_t GetArgumentCount() const; + bool empty() const { return GetArgumentCount() == 0; } + + //------------------------------------------------------------------ + /// Gets the NULL terminated C string argument pointer for the + /// argument at index \a idx. + /// + /// @return + /// The NULL terminated C string argument pointer if \a idx is a + /// valid argument index, NULL otherwise. + //------------------------------------------------------------------ + const char *GetArgumentAtIndex(size_t idx) const; + + llvm::ArrayRef entries() const { return m_entries; } + char GetArgumentQuoteCharAtIndex(size_t idx) const; + + using const_iterator = std::vector::const_iterator; + + const_iterator begin() const { return m_entries.begin(); } + const_iterator end() const { return m_entries.end(); } + + size_t size() const { return GetArgumentCount(); } + const ArgEntry &operator[](size_t n) const { return m_entries[n]; } + + //------------------------------------------------------------------ + /// Gets the argument vector. + /// + /// The value returned by this function can be used by any function + /// that takes and vector. The return value is just like \a argv + /// in the standard C entry point function: + /// \code + /// int main (int argc, const char **argv); + /// \endcode + /// + /// @return + /// An array of NULL terminated C string argument pointers that + /// also has a terminating NULL C string pointer + //------------------------------------------------------------------ + char **GetArgumentVector(); + + //------------------------------------------------------------------ + /// Gets the argument vector. + /// + /// The value returned by this function can be used by any function + /// that takes and vector. The return value is just like \a argv + /// in the standard C entry point function: + /// \code + /// int main (int argc, const char **argv); + /// \endcode + /// + /// @return + /// An array of NULL terminate C string argument pointers that + /// also has a terminating NULL C string pointer + //------------------------------------------------------------------ + const char **GetConstArgumentVector() const; + + //------------------------------------------------------------------ + /// Gets the argument as an ArrayRef. Note that the return value does *not* + /// have a nullptr const char * at the end, as the size of the list is + /// embedded in the ArrayRef object. + //------------------------------------------------------------------ + llvm::ArrayRef GetArgumentArrayRef() const { + return llvm::makeArrayRef(m_argv).drop_back(); + } + + //------------------------------------------------------------------ + /// Appends a new argument to the end of the list argument list. + /// + /// @param[in] arg_cstr + /// The new argument as a NULL terminated C string. + /// + /// @param[in] quote_char + /// If the argument was originally quoted, put in the quote char here. + //------------------------------------------------------------------ + void AppendArgument(llvm::StringRef arg_str, char quote_char = '\0'); + + void AppendArguments(const Args &rhs); + + void AppendArguments(const char **argv); + + //------------------------------------------------------------------ + /// Insert the argument value at index \a idx to \a arg_cstr. + /// + /// @param[in] idx + /// The index of where to insert the argument. + /// + /// @param[in] arg_cstr + /// The new argument as a NULL terminated C string. + /// + /// @param[in] quote_char + /// If the argument was originally quoted, put in the quote char here. + /// + /// @return + /// The NULL terminated C string of the copy of \a arg_cstr. + //------------------------------------------------------------------ + void InsertArgumentAtIndex(size_t idx, llvm::StringRef arg_str, + char quote_char = '\0'); + + //------------------------------------------------------------------ + /// Replaces the argument value at index \a idx to \a arg_cstr + /// if \a idx is a valid argument index. + /// + /// @param[in] idx + /// The index of the argument that will have its value replaced. + /// + /// @param[in] arg_cstr + /// The new argument as a NULL terminated C string. + /// + /// @param[in] quote_char + /// If the argument was originally quoted, put in the quote char here. + //------------------------------------------------------------------ + void ReplaceArgumentAtIndex(size_t idx, llvm::StringRef arg_str, + char quote_char = '\0'); + + //------------------------------------------------------------------ + /// Deletes the argument value at index + /// if \a idx is a valid argument index. + /// + /// @param[in] idx + /// The index of the argument that will have its value replaced. + /// + //------------------------------------------------------------------ + void DeleteArgumentAtIndex(size_t idx); + + //------------------------------------------------------------------ + /// Sets the argument vector value, optionally copying all + /// arguments into an internal buffer. + /// + /// Sets the arguments to match those found in \a argv. All argument + /// strings will be copied into an internal buffers. + // + // FIXME: Handle the quote character somehow. + //------------------------------------------------------------------ + void SetArguments(size_t argc, const char **argv); + + void SetArguments(const char **argv); + + //------------------------------------------------------------------ + /// Shifts the first argument C string value of the array off the + /// argument array. + /// + /// The string value will be freed, so a copy of the string should + /// be made by calling Args::GetArgumentAtIndex (size_t) const + /// first and copying the returned value before calling + /// Args::Shift(). + /// + /// @see Args::GetArgumentAtIndex (size_t) const + //------------------------------------------------------------------ + void Shift(); + + //------------------------------------------------------------------ + /// Inserts a class owned copy of \a arg_cstr at the beginning of + /// the argument vector. + /// + /// A copy \a arg_cstr will be made. + /// + /// @param[in] arg_cstr + /// The argument to push on the front of the argument stack. + /// + /// @param[in] quote_char + /// If the argument was originally quoted, put in the quote char here. + //------------------------------------------------------------------ + void Unshift(llvm::StringRef arg_str, char quote_char = '\0'); + + //------------------------------------------------------------------ + // Clear the arguments. + // + // For re-setting or blanking out the list of arguments. + //------------------------------------------------------------------ + void Clear(); + + static const char *StripSpaces(std::string &s, bool leading = true, + bool trailing = true, + bool return_null_if_empty = true); + + static bool UInt64ValueIsValidForByteSize(uint64_t uval64, + size_t total_byte_size) { + if (total_byte_size > 8) + return false; + + if (total_byte_size == 8) + return true; + + const uint64_t max = ((uint64_t)1 << (uint64_t)(total_byte_size * 8)) - 1; + return uval64 <= max; + } + + static bool SInt64ValueIsValidForByteSize(int64_t sval64, + size_t total_byte_size) { + if (total_byte_size > 8) + return false; + + if (total_byte_size == 8) + return true; + + const int64_t max = ((int64_t)1 << (uint64_t)(total_byte_size * 8 - 1)) - 1; + const int64_t min = ~(max); + return min <= sval64 && sval64 <= max; + } + + static lldb::Encoding + StringToEncoding(llvm::StringRef s, + lldb::Encoding fail_value = lldb::eEncodingInvalid); + + static uint32_t StringToGenericRegister(llvm::StringRef s); + + static bool StringToVersion(llvm::StringRef string, uint32_t &major, + uint32_t &minor, uint32_t &update); + + static const char *GetShellSafeArgument(const FileSpec &shell, + const char *unsafe_arg, + std::string &safe_arg); + + // EncodeEscapeSequences will change the textual representation of common + // escape sequences like "\n" (two characters) into a single '\n'. It does + // this for all of the supported escaped sequences and for the \0ooo (octal) + // and \xXX (hex). The resulting "dst" string will contain the character + // versions of all supported escape sequences. The common supported escape + // sequences are: "\a", "\b", "\f", "\n", "\r", "\t", "\v", "\'", "\"", "\\". + + static void EncodeEscapeSequences(const char *src, std::string &dst); + + // ExpandEscapeSequences will change a string of possibly non-printable + // characters and expand them into text. So '\n' will turn into two characters + // like "\n" which is suitable for human reading. When a character is not + // printable and isn't one of the common in escape sequences listed in the + // help for EncodeEscapeSequences, then it will be encoded as octal. Printable + // characters are left alone. + static void ExpandEscapedCharacters(const char *src, std::string &dst); + + static std::string EscapeLLDBCommandArgument(const std::string &arg, + char quote_char); + +private: + std::vector m_entries; + std::vector m_argv; +}; + +} // namespace lldb_private + +#endif // LLDB_UTILITY_ARGS_H Index: lldb/trunk/source/API/SBDebugger.cpp =================================================================== --- lldb/trunk/source/API/SBDebugger.cpp +++ lldb/trunk/source/API/SBDebugger.cpp @@ -45,12 +45,12 @@ #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/Host/XML.h" #include "lldb/Initialization/SystemLifetimeManager.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Interpreter/OptionGroupPlatform.h" #include "lldb/Target/Process.h" #include "lldb/Target/TargetList.h" +#include "lldb/Utility/Args.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" Index: lldb/trunk/source/API/SBPlatform.cpp =================================================================== --- lldb/trunk/source/API/SBPlatform.cpp +++ lldb/trunk/source/API/SBPlatform.cpp @@ -13,10 +13,10 @@ #include "lldb/API/SBLaunchInfo.h" #include "lldb/API/SBUnixSignals.h" #include "lldb/Host/File.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Target.h" #include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Status.h" #include "llvm/Support/FileSystem.h" Index: lldb/trunk/source/API/SBProcess.cpp =================================================================== --- lldb/trunk/source/API/SBProcess.cpp +++ lldb/trunk/source/API/SBProcess.cpp @@ -20,13 +20,13 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" Index: lldb/trunk/source/API/SBTarget.cpp =================================================================== --- lldb/trunk/source/API/SBTarget.cpp +++ lldb/trunk/source/API/SBTarget.cpp @@ -44,7 +44,6 @@ #include "lldb/Core/ValueObjectList.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/Host/Host.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/DeclVendor.h" #include "lldb/Symbol/ObjectFile.h" @@ -60,6 +59,7 @@ #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegularExpression.h" Index: lldb/trunk/source/Breakpoint/BreakpointIDList.cpp =================================================================== --- lldb/trunk/source/Breakpoint/BreakpointIDList.cpp +++ lldb/trunk/source/Breakpoint/BreakpointIDList.cpp @@ -16,9 +16,9 @@ #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/Args.h" using namespace lldb; using namespace lldb_private; Index: lldb/trunk/source/Commands/CommandCompletions.cpp =================================================================== --- lldb/trunk/source/Commands/CommandCompletions.cpp +++ lldb/trunk/source/Commands/CommandCompletions.cpp @@ -23,13 +23,13 @@ #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Host/FileSystem.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Variable.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/TildeExpressionResolver.h" Index: lldb/trunk/source/Commands/CommandObjectApropos.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectApropos.cpp +++ lldb/trunk/source/Commands/CommandObjectApropos.cpp @@ -13,11 +13,11 @@ // Other libraries and framework includes // Project includes #include "CommandObjectApropos.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/Property.h" +#include "lldb/Utility/Args.h" using namespace lldb; using namespace lldb_private; Index: lldb/trunk/source/Commands/CommandObjectCommands.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectCommands.cpp +++ lldb/trunk/source/Commands/CommandObjectCommands.cpp @@ -18,7 +18,6 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/IOHandler.h" #include "lldb/Host/OptionParser.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandHistory.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObjectRegexCommand.h" @@ -29,6 +28,7 @@ #include "lldb/Interpreter/OptionValueUInt64.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/StringList.h" using namespace lldb; Index: lldb/trunk/source/Commands/CommandObjectFrame.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectFrame.cpp +++ lldb/trunk/source/Commands/CommandObjectFrame.cpp @@ -24,7 +24,6 @@ #include "lldb/DataFormatters/ValueObjectPrinter.h" #include "lldb/Host/Host.h" #include "lldb/Host/OptionParser.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupFormat.h" @@ -44,6 +43,7 @@ #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" Index: lldb/trunk/source/Commands/CommandObjectLog.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectLog.cpp +++ lldb/trunk/source/Commands/CommandObjectLog.cpp @@ -16,7 +16,6 @@ #include "lldb/Core/Module.h" #include "lldb/Core/StreamFile.h" #include "lldb/Host/OptionParser.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" @@ -27,6 +26,7 @@ #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegularExpression.h" Index: lldb/trunk/source/Commands/CommandObjectMemory.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectMemory.cpp +++ lldb/trunk/source/Commands/CommandObjectMemory.cpp @@ -24,7 +24,6 @@ #include "lldb/Core/ValueObjectMemory.h" #include "lldb/DataFormatters/ValueObjectPrinter.h" #include "lldb/Host/OptionParser.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" @@ -41,6 +40,7 @@ #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Thread.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataBufferLLVM.h" #include "lldb/Utility/StreamString.h" Index: lldb/trunk/source/Commands/CommandObjectPlatform.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectPlatform.cpp +++ lldb/trunk/source/Commands/CommandObjectPlatform.cpp @@ -18,7 +18,6 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/StringConvert.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandOptionValidators.h" #include "lldb/Interpreter/CommandReturnObject.h" @@ -27,6 +26,7 @@ #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/DataExtractor.h" #include "llvm/ADT/SmallString.h" Index: lldb/trunk/source/Commands/CommandObjectProcess.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectProcess.cpp +++ lldb/trunk/source/Commands/CommandObjectProcess.cpp @@ -21,7 +21,6 @@ #include "lldb/Host/Host.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/StringConvert.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" @@ -32,6 +31,7 @@ #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/Args.h" using namespace lldb; using namespace lldb_private; Index: lldb/trunk/source/Commands/CommandObjectRegister.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectRegister.cpp +++ lldb/trunk/source/Commands/CommandObjectRegister.cpp @@ -12,7 +12,6 @@ #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Scalar.h" #include "lldb/Host/OptionParser.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupFormat.h" @@ -25,6 +24,7 @@ #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Thread.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/DataExtractor.h" #include "llvm/Support/Errno.h" Index: lldb/trunk/source/Commands/CommandObjectTarget.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectTarget.cpp +++ lldb/trunk/source/Commands/CommandObjectTarget.cpp @@ -21,7 +21,6 @@ #include "lldb/Host/OptionParser.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/Symbols.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" @@ -51,6 +50,7 @@ #include "lldb/Target/StackFrame.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadSpec.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Timer.h" #include "llvm/Support/FileSystem.h" Index: lldb/trunk/source/Core/RegisterValue.cpp =================================================================== --- lldb/trunk/source/Core/RegisterValue.cpp +++ lldb/trunk/source/Core/RegisterValue.cpp @@ -11,7 +11,7 @@ #include "lldb/Core/DumpDataExtractor.h" #include "lldb/Core/Scalar.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/Stream.h" Index: lldb/trunk/source/Host/CMakeLists.txt =================================================================== --- lldb/trunk/source/Host/CMakeLists.txt +++ lldb/trunk/source/Host/CMakeLists.txt @@ -180,7 +180,6 @@ LINK_LIBS lldbCore - lldbInterpreter lldbSymbol lldbTarget lldbUtility Index: lldb/trunk/source/Host/macosx/HostInfoMacOSX.mm =================================================================== --- lldb/trunk/source/Host/macosx/HostInfoMacOSX.mm +++ lldb/trunk/source/Host/macosx/HostInfoMacOSX.mm @@ -13,7 +13,7 @@ #include "lldb/Host/HostInfo.h" #include "lldb/Host/macosx/HostInfoMacOSX.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/SafeMachO.h" Index: lldb/trunk/source/Interpreter/Args.cpp =================================================================== --- lldb/trunk/source/Interpreter/Args.cpp +++ lldb/trunk/source/Interpreter/Args.cpp @@ -1,694 +0,0 @@ -//===-- Args.cpp ------------------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -// C Includes -#include -// C++ Includes -// Other libraries and framework includes -// Project includes -#include "lldb/Interpreter/Args.h" -#include "lldb/Utility/ConstString.h" -#include "lldb/Utility/FileSpec.h" -#include "lldb/Utility/Stream.h" -#include "lldb/Utility/StringList.h" - -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringSwitch.h" - -using namespace lldb; -using namespace lldb_private; - - -// A helper function for argument parsing. -// Parses the initial part of the first argument using normal double quote -// rules: -// backslash escapes the double quote and itself. The parsed string is appended -// to the second -// argument. The function returns the unparsed portion of the string, starting -// at the closing -// quote. -static llvm::StringRef ParseDoubleQuotes(llvm::StringRef quoted, - std::string &result) { - // Inside double quotes, '\' and '"' are special. - static const char *k_escapable_characters = "\"\\"; - while (true) { - // Skip over over regular characters and append them. - size_t regular = quoted.find_first_of(k_escapable_characters); - result += quoted.substr(0, regular); - quoted = quoted.substr(regular); - - // If we have reached the end of string or the closing quote, we're done. - if (quoted.empty() || quoted.front() == '"') - break; - - // We have found a backslash. - quoted = quoted.drop_front(); - - if (quoted.empty()) { - // A lone backslash at the end of string, let's just append it. - result += '\\'; - break; - } - - // If the character after the backslash is not a whitelisted escapable - // character, we - // leave the character sequence untouched. - if (strchr(k_escapable_characters, quoted.front()) == nullptr) - result += '\\'; - - result += quoted.front(); - quoted = quoted.drop_front(); - } - - return quoted; -} - -static size_t ArgvToArgc(const char **argv) { - if (!argv) - return 0; - size_t count = 0; - while (*argv++) - ++count; - return count; -} - -// A helper function for SetCommandString. Parses a single argument from the -// command string, processing quotes and backslashes in a shell-like manner. -// The function returns a tuple consisting of the parsed argument, the quote -// char used, and the unparsed portion of the string starting at the first -// unqouted, unescaped whitespace character. -static std::tuple -ParseSingleArgument(llvm::StringRef command) { - // Argument 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. - 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'; - - bool arg_complete = false; - do { - // Skip over over regular characters and append them. - size_t regular = command.find_first_of(" \t\"'`\\"); - arg += command.substr(0, regular); - command = command.substr(regular); - - if (command.empty()) - break; - - char special = command.front(); - command = command.drop_front(); - switch (special) { - case '\\': - if (command.empty()) { - arg += '\\'; - break; - } - - // If the character after the backslash is not a whitelisted escapable - // character, we - // leave the character sequence untouched. - if (strchr(" \t\\'\"`", command.front()) == nullptr) - arg += '\\'; - - arg += command.front(); - command = command.drop_front(); - - break; - - case ' ': - case '\t': - // We are not inside any quotes, we just found a space after an - // argument. We are done. - arg_complete = true; - break; - - case '"': - case '\'': - case '`': - // We found the start of a quote scope. - if (first_quote_char == '\0') - first_quote_char = special; - - if (special == '"') - command = ParseDoubleQuotes(command, arg); - else { - // For single quotes, we simply skip ahead to the matching quote - // character - // (or the end of the string). - size_t quoted = command.find(special); - arg += command.substr(0, quoted); - command = command.substr(quoted); - } - - // If we found a closing quote, skip it. - if (!command.empty()) - command = command.drop_front(); - - break; - } - } while (!arg_complete); - - return std::make_tuple(arg, first_quote_char, command); -} - -Args::ArgEntry::ArgEntry(llvm::StringRef str, char quote) : quote(quote) { - size_t size = str.size(); - ptr.reset(new char[size + 1]); - - ::memcpy(data(), str.data() ? str.data() : "", size); - ptr[size] = 0; - ref = llvm::StringRef(c_str(), size); -} - -//---------------------------------------------------------------------- -// Args constructor -//---------------------------------------------------------------------- -Args::Args(llvm::StringRef command) { SetCommandString(command); } - -Args::Args(const Args &rhs) { *this = rhs; } - -Args::Args(const StringList &list) : Args() { - for(size_t i = 0; i < list.GetSize(); ++i) - AppendArgument(list[i]); -} - -Args &Args::operator=(const Args &rhs) { - Clear(); - - m_argv.clear(); - m_entries.clear(); - for (auto &entry : rhs.m_entries) { - m_entries.emplace_back(entry.ref, entry.quote); - m_argv.push_back(m_entries.back().data()); - } - m_argv.push_back(nullptr); - return *this; -} - -//---------------------------------------------------------------------- -// Destructor -//---------------------------------------------------------------------- -Args::~Args() {} - -void Args::Dump(Stream &s, const char *label_name) const { - if (!label_name) - return; - - int i = 0; - for (auto &entry : m_entries) { - s.Indent(); - s.Format("{0}[{1}]=\"{2}\"\n", label_name, i++, entry.ref); - } - s.Format("{0}[{1}]=NULL\n", label_name, i); - s.EOL(); -} - -bool Args::GetCommandString(std::string &command) const { - command.clear(); - - for (size_t i = 0; i < m_entries.size(); ++i) { - if (i > 0) - command += ' '; - command += m_entries[i].ref; - } - - return !m_entries.empty(); -} - -bool Args::GetQuotedCommandString(std::string &command) const { - command.clear(); - - for (size_t i = 0; i < m_entries.size(); ++i) { - if (i > 0) - command += ' '; - - if (m_entries[i].quote) { - command += m_entries[i].quote; - command += m_entries[i].ref; - command += m_entries[i].quote; - } else { - command += m_entries[i].ref; - } - } - - return !m_entries.empty(); -} - -void Args::SetCommandString(llvm::StringRef command) { - Clear(); - m_argv.clear(); - - static const char *k_space_separators = " \t"; - command = command.ltrim(k_space_separators); - std::string arg; - char quote; - while (!command.empty()) { - std::tie(arg, quote, command) = ParseSingleArgument(command); - m_entries.emplace_back(arg, quote); - m_argv.push_back(m_entries.back().data()); - command = command.ltrim(k_space_separators); - } - m_argv.push_back(nullptr); -} - -size_t Args::GetArgumentCount() const { return m_entries.size(); } - -const char *Args::GetArgumentAtIndex(size_t idx) const { - if (idx < m_argv.size()) - return m_argv[idx]; - return nullptr; -} - -char Args::GetArgumentQuoteCharAtIndex(size_t idx) const { - if (idx < m_entries.size()) - return m_entries[idx].quote; - return '\0'; -} - -char **Args::GetArgumentVector() { - assert(!m_argv.empty()); - // TODO: functions like execve and posix_spawnp exhibit undefined behavior - // when argv or envp is null. So the code below is actually wrong. However, - // other code in LLDB depends on it being null. The code has been acting this - // way for some time, so it makes sense to leave it this way until someone - // has the time to come along and fix it. - return (m_argv.size() > 1) ? m_argv.data() : nullptr; -} - -const char **Args::GetConstArgumentVector() const { - assert(!m_argv.empty()); - return (m_argv.size() > 1) ? const_cast(m_argv.data()) - : nullptr; -} - -void Args::Shift() { - // Don't pop the last NULL terminator from the argv array - if (m_entries.empty()) - return; - m_argv.erase(m_argv.begin()); - m_entries.erase(m_entries.begin()); -} - -void Args::Unshift(llvm::StringRef arg_str, char quote_char) { - InsertArgumentAtIndex(0, arg_str, quote_char); -} - -void Args::AppendArguments(const Args &rhs) { - assert(m_argv.size() == m_entries.size() + 1); - assert(m_argv.back() == nullptr); - m_argv.pop_back(); - for (auto &entry : rhs.m_entries) { - m_entries.emplace_back(entry.ref, entry.quote); - m_argv.push_back(m_entries.back().data()); - } - m_argv.push_back(nullptr); -} - -void Args::AppendArguments(const char **argv) { - size_t argc = ArgvToArgc(argv); - - assert(m_argv.size() == m_entries.size() + 1); - assert(m_argv.back() == nullptr); - m_argv.pop_back(); - for (auto arg : llvm::makeArrayRef(argv, argc)) { - m_entries.emplace_back(arg, '\0'); - m_argv.push_back(m_entries.back().data()); - } - - m_argv.push_back(nullptr); -} - -void Args::AppendArgument(llvm::StringRef arg_str, char quote_char) { - InsertArgumentAtIndex(GetArgumentCount(), arg_str, quote_char); -} - -void Args::InsertArgumentAtIndex(size_t idx, llvm::StringRef arg_str, - char quote_char) { - assert(m_argv.size() == m_entries.size() + 1); - assert(m_argv.back() == nullptr); - - if (idx > m_entries.size()) - return; - m_entries.emplace(m_entries.begin() + idx, arg_str, quote_char); - m_argv.insert(m_argv.begin() + idx, m_entries[idx].data()); -} - -void Args::ReplaceArgumentAtIndex(size_t idx, llvm::StringRef arg_str, - char quote_char) { - assert(m_argv.size() == m_entries.size() + 1); - assert(m_argv.back() == nullptr); - - if (idx >= m_entries.size()) - return; - - if (arg_str.size() > m_entries[idx].ref.size()) { - m_entries[idx] = ArgEntry(arg_str, quote_char); - m_argv[idx] = m_entries[idx].data(); - } else { - const char *src_data = arg_str.data() ? arg_str.data() : ""; - ::memcpy(m_entries[idx].data(), src_data, arg_str.size()); - m_entries[idx].ptr[arg_str.size()] = 0; - m_entries[idx].ref = m_entries[idx].ref.take_front(arg_str.size()); - } -} - -void Args::DeleteArgumentAtIndex(size_t idx) { - if (idx >= m_entries.size()) - return; - - m_argv.erase(m_argv.begin() + idx); - m_entries.erase(m_entries.begin() + idx); -} - -void Args::SetArguments(size_t argc, const char **argv) { - Clear(); - - auto args = llvm::makeArrayRef(argv, argc); - m_entries.resize(argc); - m_argv.resize(argc + 1); - for (size_t i = 0; i < args.size(); ++i) { - char quote = - ((args[i][0] == '\'') || (args[i][0] == '"') || (args[i][0] == '`')) - ? args[i][0] - : '\0'; - - m_entries[i] = ArgEntry(args[i], quote); - m_argv[i] = m_entries[i].data(); - } -} - -void Args::SetArguments(const char **argv) { - SetArguments(ArgvToArgc(argv), argv); -} - -void Args::Clear() { - m_entries.clear(); - m_argv.clear(); - m_argv.push_back(nullptr); -} - -const char *Args::StripSpaces(std::string &s, bool leading, bool trailing, - bool return_null_if_empty) { - static const char *k_white_space = " \t\v"; - if (!s.empty()) { - if (leading) { - size_t pos = s.find_first_not_of(k_white_space); - if (pos == std::string::npos) - s.clear(); - else if (pos > 0) - s.erase(0, pos); - } - - if (trailing) { - size_t rpos = s.find_last_not_of(k_white_space); - if (rpos != std::string::npos && rpos + 1 < s.size()) - s.erase(rpos + 1); - } - } - if (return_null_if_empty && s.empty()) - return nullptr; - return s.c_str(); -} - -bool Args::StringToVersion(llvm::StringRef string, uint32_t &major, - uint32_t &minor, uint32_t &update) { - major = UINT32_MAX; - minor = UINT32_MAX; - update = UINT32_MAX; - - if (string.empty()) - return false; - - llvm::StringRef major_str, minor_str, update_str; - - std::tie(major_str, minor_str) = string.split('.'); - std::tie(minor_str, update_str) = minor_str.split('.'); - if (major_str.getAsInteger(10, major)) - return false; - if (!minor_str.empty() && minor_str.getAsInteger(10, minor)) - return false; - if (!update_str.empty() && update_str.getAsInteger(10, update)) - return false; - - return true; -} - -const char *Args::GetShellSafeArgument(const FileSpec &shell, - const char *unsafe_arg, - std::string &safe_arg) { - struct ShellDescriptor { - ConstString m_basename; - const char *m_escapables; - }; - - static ShellDescriptor g_Shells[] = {{ConstString("bash"), " '\"<>()&"}, - {ConstString("tcsh"), " '\"<>()&$"}, - {ConstString("sh"), " '\"<>()&"}}; - - // safe minimal set - const char *escapables = " '\""; - - if (auto basename = shell.GetFilename()) { - for (const auto &Shell : g_Shells) { - if (Shell.m_basename == basename) { - escapables = Shell.m_escapables; - break; - } - } - } - - 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(escapables, prev_pos); - if (pos != std::string::npos) { - safe_arg.insert(pos, 1, '\\'); - prev_pos = pos + 2; - } else - break; - } - return safe_arg.c_str(); -} - -lldb::Encoding Args::StringToEncoding(llvm::StringRef s, - lldb::Encoding fail_value) { - return llvm::StringSwitch(s) - .Case("uint", eEncodingUint) - .Case("sint", eEncodingSint) - .Case("ieee754", eEncodingIEEE754) - .Case("vector", eEncodingVector) - .Default(fail_value); -} - -uint32_t Args::StringToGenericRegister(llvm::StringRef s) { - if (s.empty()) - return LLDB_INVALID_REGNUM; - uint32_t result = llvm::StringSwitch(s) - .Case("pc", LLDB_REGNUM_GENERIC_PC) - .Case("sp", LLDB_REGNUM_GENERIC_SP) - .Case("fp", LLDB_REGNUM_GENERIC_FP) - .Cases("ra", "lr", LLDB_REGNUM_GENERIC_RA) - .Case("flags", LLDB_REGNUM_GENERIC_FLAGS) - .Case("arg1", LLDB_REGNUM_GENERIC_ARG1) - .Case("arg2", LLDB_REGNUM_GENERIC_ARG2) - .Case("arg3", LLDB_REGNUM_GENERIC_ARG3) - .Case("arg4", LLDB_REGNUM_GENERIC_ARG4) - .Case("arg5", LLDB_REGNUM_GENERIC_ARG5) - .Case("arg6", LLDB_REGNUM_GENERIC_ARG6) - .Case("arg7", LLDB_REGNUM_GENERIC_ARG7) - .Case("arg8", LLDB_REGNUM_GENERIC_ARG8) - .Default(LLDB_INVALID_REGNUM); - return result; -} - -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, nullptr, 8); - if (octal_value <= UINT8_MAX) { - dst.append(1, (char)octal_value); - } - } - 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, nullptr, 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 (isprint(*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; - } - } - } - } -} - -std::string Args::EscapeLLDBCommandArgument(const std::string &arg, - char quote_char) { - const char *chars_to_escape = nullptr; - switch (quote_char) { - case '\0': - chars_to_escape = " \t\\'\"`"; - break; - case '\'': - chars_to_escape = ""; - break; - case '"': - chars_to_escape = "$\"`\\"; - break; - default: - assert(false && "Unhandled quote character"); - } - - std::string res; - res.reserve(arg.size()); - for (char c : arg) { - if (::strchr(chars_to_escape, c)) - res.push_back('\\'); - res.push_back(c); - } - return res; -} Index: lldb/trunk/source/Interpreter/CMakeLists.txt =================================================================== --- lldb/trunk/source/Interpreter/CMakeLists.txt +++ lldb/trunk/source/Interpreter/CMakeLists.txt @@ -1,5 +1,4 @@ add_lldb_library(lldbInterpreter - Args.cpp CommandAlias.cpp CommandHistory.cpp CommandInterpreter.cpp Index: lldb/trunk/source/Interpreter/CommandInterpreter.cpp =================================================================== --- lldb/trunk/source/Interpreter/CommandInterpreter.cpp +++ lldb/trunk/source/Interpreter/CommandInterpreter.cpp @@ -54,13 +54,13 @@ #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/Property.h" +#include "lldb/Utility/Args.h" #include "lldb/Target/Process.h" #include "lldb/Target/TargetList.h" Index: lldb/trunk/source/Interpreter/CommandObjectScript.cpp =================================================================== --- lldb/trunk/source/Interpreter/CommandObjectScript.cpp +++ lldb/trunk/source/Interpreter/CommandObjectScript.cpp @@ -18,10 +18,10 @@ #include "lldb/DataFormatters/DataVisualization.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Utility/Args.h" using namespace lldb; using namespace lldb_private; Index: lldb/trunk/source/Interpreter/OptionValueArch.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueArch.cpp +++ lldb/trunk/source/Interpreter/OptionValueArch.cpp @@ -15,9 +15,9 @@ // Project includes #include "lldb/Core/State.h" #include "lldb/DataFormatters/FormatManager.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Utility/Args.h" using namespace lldb; using namespace lldb_private; Index: lldb/trunk/source/Interpreter/OptionValueArgs.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueArgs.cpp +++ lldb/trunk/source/Interpreter/OptionValueArgs.cpp @@ -13,7 +13,7 @@ // C++ Includes // Other libraries and framework includes // Project includes -#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Args.h" using namespace lldb; using namespace lldb_private; Index: lldb/trunk/source/Interpreter/OptionValueArray.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueArray.cpp +++ lldb/trunk/source/Interpreter/OptionValueArray.cpp @@ -14,7 +14,7 @@ // Other libraries and framework includes // Project includes #include "lldb/Host/StringConvert.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" using namespace lldb; Index: lldb/trunk/source/Interpreter/OptionValueDictionary.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueDictionary.cpp +++ lldb/trunk/source/Interpreter/OptionValueDictionary.cpp @@ -16,8 +16,8 @@ // Project includes #include "lldb/Core/State.h" #include "lldb/DataFormatters/FormatManager.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Utility/Args.h" using namespace lldb; using namespace lldb_private; Index: lldb/trunk/source/Interpreter/OptionValueFileSpec.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueFileSpec.cpp +++ lldb/trunk/source/Interpreter/OptionValueFileSpec.cpp @@ -12,9 +12,9 @@ #include "lldb/Core/State.h" #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Host/FileSystem.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/DataBufferLLVM.h" using namespace lldb; Index: lldb/trunk/source/Interpreter/OptionValueFileSpecLIst.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueFileSpecLIst.cpp +++ lldb/trunk/source/Interpreter/OptionValueFileSpecLIst.cpp @@ -14,7 +14,7 @@ // Other libraries and framework includes // Project includes #include "lldb/Host/StringConvert.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" using namespace lldb; Index: lldb/trunk/source/Interpreter/OptionValueLanguage.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueLanguage.cpp +++ lldb/trunk/source/Interpreter/OptionValueLanguage.cpp @@ -14,8 +14,8 @@ // Other libraries and framework includes // Project includes #include "lldb/DataFormatters/FormatManager.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Target/Language.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" using namespace lldb; Index: lldb/trunk/source/Interpreter/OptionValuePathMappings.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValuePathMappings.cpp +++ lldb/trunk/source/Interpreter/OptionValuePathMappings.cpp @@ -14,7 +14,7 @@ // Other libraries and framework includes // Project includes #include "lldb/Host/StringConvert.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Stream.h" Index: lldb/trunk/source/Interpreter/OptionValueProperties.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueProperties.cpp +++ lldb/trunk/source/Interpreter/OptionValueProperties.cpp @@ -16,9 +16,9 @@ #include "lldb/Utility/Flags.h" #include "lldb/Core/UserSettingsController.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/OptionValues.h" #include "lldb/Interpreter/Property.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" Index: lldb/trunk/source/Interpreter/OptionValueString.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueString.cpp +++ lldb/trunk/source/Interpreter/OptionValueString.cpp @@ -15,7 +15,7 @@ // Other libraries and framework includes // Project includes #include "lldb/Host/OptionParser.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" using namespace lldb; Index: lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp =================================================================== --- lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp +++ lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp @@ -26,7 +26,6 @@ #include "lldb/Expression/UserExpression.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/StringConvert.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" @@ -40,6 +39,7 @@ #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/DataBufferLLVM.h" #include "lldb/Utility/Log.h" Index: lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp =================================================================== --- lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp +++ lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp @@ -10,7 +10,6 @@ #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/PluginManager.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" @@ -20,6 +19,7 @@ #include "lldb/Symbol/VariableList.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" Index: lldb/trunk/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.h =================================================================== --- lldb/trunk/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.h +++ lldb/trunk/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.h @@ -24,8 +24,8 @@ typedef void *id; #endif // Project includes -#include "lldb/Interpreter/Args.h" #include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Status.h" Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -24,7 +24,7 @@ #include "lldb/Core/Listener.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/Predicate.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Args.h" #include "lldb/lldb-public.h" #include "lldb/Utility/StringExtractorGDBRemote.h" Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -22,11 +22,11 @@ #include "lldb/Core/State.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/XML.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/JSON.h" #include "lldb/Utility/LLDBAssert.h" Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -33,9 +33,9 @@ #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Host/common/NativeThreadProtocol.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Target/FileAction.h" #include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/Endian.h" #include "lldb/Utility/JSON.h" Index: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -47,7 +47,6 @@ #include "lldb/Host/Symbols.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/XML.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" @@ -66,6 +65,7 @@ #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/CleanUp.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/StreamString.h" Index: lldb/trunk/source/Utility/Args.cpp =================================================================== --- lldb/trunk/source/Utility/Args.cpp +++ lldb/trunk/source/Utility/Args.cpp @@ -0,0 +1,685 @@ +//===-- 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/Utility/Args.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StringList.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace lldb; +using namespace lldb_private; + +// A helper function for argument parsing. +// Parses the initial part of the first argument using normal double quote +// rules: +// backslash escapes the double quote and itself. The parsed string is appended +// to the second +// argument. The function returns the unparsed portion of the string, starting +// at the closing +// quote. +static llvm::StringRef ParseDoubleQuotes(llvm::StringRef quoted, + std::string &result) { + // Inside double quotes, '\' and '"' are special. + static const char *k_escapable_characters = "\"\\"; + while (true) { + // Skip over over regular characters and append them. + size_t regular = quoted.find_first_of(k_escapable_characters); + result += quoted.substr(0, regular); + quoted = quoted.substr(regular); + + // If we have reached the end of string or the closing quote, we're done. + if (quoted.empty() || quoted.front() == '"') + break; + + // We have found a backslash. + quoted = quoted.drop_front(); + + if (quoted.empty()) { + // A lone backslash at the end of string, let's just append it. + result += '\\'; + break; + } + + // If the character after the backslash is not a whitelisted escapable + // character, we + // leave the character sequence untouched. + if (strchr(k_escapable_characters, quoted.front()) == nullptr) + result += '\\'; + + result += quoted.front(); + quoted = quoted.drop_front(); + } + + return quoted; +} + +static size_t ArgvToArgc(const char **argv) { + if (!argv) + return 0; + size_t count = 0; + while (*argv++) + ++count; + return count; +} + +// A helper function for SetCommandString. Parses a single argument from the +// command string, processing quotes and backslashes in a shell-like manner. +// The function returns a tuple consisting of the parsed argument, the quote +// char used, and the unparsed portion of the string starting at the first +// unqouted, unescaped whitespace character. +static std::tuple +ParseSingleArgument(llvm::StringRef command) { + // Argument 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. + 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'; + + bool arg_complete = false; + do { + // Skip over over regular characters and append them. + size_t regular = command.find_first_of(" \t\"'`\\"); + arg += command.substr(0, regular); + command = command.substr(regular); + + if (command.empty()) + break; + + char special = command.front(); + command = command.drop_front(); + switch (special) { + case '\\': + if (command.empty()) { + arg += '\\'; + break; + } + + // If the character after the backslash is not a whitelisted escapable + // character, we + // leave the character sequence untouched. + if (strchr(" \t\\'\"`", command.front()) == nullptr) + arg += '\\'; + + arg += command.front(); + command = command.drop_front(); + + break; + + case ' ': + case '\t': + // We are not inside any quotes, we just found a space after an + // argument. We are done. + arg_complete = true; + break; + + case '"': + case '\'': + case '`': + // We found the start of a quote scope. + if (first_quote_char == '\0') + first_quote_char = special; + + if (special == '"') + command = ParseDoubleQuotes(command, arg); + else { + // For single quotes, we simply skip ahead to the matching quote + // character + // (or the end of the string). + size_t quoted = command.find(special); + arg += command.substr(0, quoted); + command = command.substr(quoted); + } + + // If we found a closing quote, skip it. + if (!command.empty()) + command = command.drop_front(); + + break; + } + } while (!arg_complete); + + return std::make_tuple(arg, first_quote_char, command); +} + +Args::ArgEntry::ArgEntry(llvm::StringRef str, char quote) : quote(quote) { + size_t size = str.size(); + ptr.reset(new char[size + 1]); + + ::memcpy(data(), str.data() ? str.data() : "", size); + ptr[size] = 0; + ref = llvm::StringRef(c_str(), size); +} + +//---------------------------------------------------------------------- +// Args constructor +//---------------------------------------------------------------------- +Args::Args(llvm::StringRef command) { SetCommandString(command); } + +Args::Args(const Args &rhs) { *this = rhs; } + +Args::Args(const StringList &list) : Args() { + for (size_t i = 0; i < list.GetSize(); ++i) + AppendArgument(list[i]); +} + +Args &Args::operator=(const Args &rhs) { + Clear(); + + m_argv.clear(); + m_entries.clear(); + for (auto &entry : rhs.m_entries) { + m_entries.emplace_back(entry.ref, entry.quote); + m_argv.push_back(m_entries.back().data()); + } + m_argv.push_back(nullptr); + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Args::~Args() {} + +void Args::Dump(Stream &s, const char *label_name) const { + if (!label_name) + return; + + int i = 0; + for (auto &entry : m_entries) { + s.Indent(); + s.Format("{0}[{1}]=\"{2}\"\n", label_name, i++, entry.ref); + } + s.Format("{0}[{1}]=NULL\n", label_name, i); + s.EOL(); +} + +bool Args::GetCommandString(std::string &command) const { + command.clear(); + + for (size_t i = 0; i < m_entries.size(); ++i) { + if (i > 0) + command += ' '; + command += m_entries[i].ref; + } + + return !m_entries.empty(); +} + +bool Args::GetQuotedCommandString(std::string &command) const { + command.clear(); + + for (size_t i = 0; i < m_entries.size(); ++i) { + if (i > 0) + command += ' '; + + if (m_entries[i].quote) { + command += m_entries[i].quote; + command += m_entries[i].ref; + command += m_entries[i].quote; + } else { + command += m_entries[i].ref; + } + } + + return !m_entries.empty(); +} + +void Args::SetCommandString(llvm::StringRef command) { + Clear(); + m_argv.clear(); + + static const char *k_space_separators = " \t"; + command = command.ltrim(k_space_separators); + std::string arg; + char quote; + while (!command.empty()) { + std::tie(arg, quote, command) = ParseSingleArgument(command); + m_entries.emplace_back(arg, quote); + m_argv.push_back(m_entries.back().data()); + command = command.ltrim(k_space_separators); + } + m_argv.push_back(nullptr); +} + +size_t Args::GetArgumentCount() const { return m_entries.size(); } + +const char *Args::GetArgumentAtIndex(size_t idx) const { + if (idx < m_argv.size()) + return m_argv[idx]; + return nullptr; +} + +char Args::GetArgumentQuoteCharAtIndex(size_t idx) const { + if (idx < m_entries.size()) + return m_entries[idx].quote; + return '\0'; +} + +char **Args::GetArgumentVector() { + assert(!m_argv.empty()); + // TODO: functions like execve and posix_spawnp exhibit undefined behavior + // when argv or envp is null. So the code below is actually wrong. However, + // other code in LLDB depends on it being null. The code has been acting this + // way for some time, so it makes sense to leave it this way until someone + // has the time to come along and fix it. + return (m_argv.size() > 1) ? m_argv.data() : nullptr; +} + +const char **Args::GetConstArgumentVector() const { + assert(!m_argv.empty()); + return (m_argv.size() > 1) ? const_cast(m_argv.data()) + : nullptr; +} + +void Args::Shift() { + // Don't pop the last NULL terminator from the argv array + if (m_entries.empty()) + return; + m_argv.erase(m_argv.begin()); + m_entries.erase(m_entries.begin()); +} + +void Args::Unshift(llvm::StringRef arg_str, char quote_char) { + InsertArgumentAtIndex(0, arg_str, quote_char); +} + +void Args::AppendArguments(const Args &rhs) { + assert(m_argv.size() == m_entries.size() + 1); + assert(m_argv.back() == nullptr); + m_argv.pop_back(); + for (auto &entry : rhs.m_entries) { + m_entries.emplace_back(entry.ref, entry.quote); + m_argv.push_back(m_entries.back().data()); + } + m_argv.push_back(nullptr); +} + +void Args::AppendArguments(const char **argv) { + size_t argc = ArgvToArgc(argv); + + assert(m_argv.size() == m_entries.size() + 1); + assert(m_argv.back() == nullptr); + m_argv.pop_back(); + for (auto arg : llvm::makeArrayRef(argv, argc)) { + m_entries.emplace_back(arg, '\0'); + m_argv.push_back(m_entries.back().data()); + } + + m_argv.push_back(nullptr); +} + +void Args::AppendArgument(llvm::StringRef arg_str, char quote_char) { + InsertArgumentAtIndex(GetArgumentCount(), arg_str, quote_char); +} + +void Args::InsertArgumentAtIndex(size_t idx, llvm::StringRef arg_str, + char quote_char) { + assert(m_argv.size() == m_entries.size() + 1); + assert(m_argv.back() == nullptr); + + if (idx > m_entries.size()) + return; + m_entries.emplace(m_entries.begin() + idx, arg_str, quote_char); + m_argv.insert(m_argv.begin() + idx, m_entries[idx].data()); +} + +void Args::ReplaceArgumentAtIndex(size_t idx, llvm::StringRef arg_str, + char quote_char) { + assert(m_argv.size() == m_entries.size() + 1); + assert(m_argv.back() == nullptr); + + if (idx >= m_entries.size()) + return; + + if (arg_str.size() > m_entries[idx].ref.size()) { + m_entries[idx] = ArgEntry(arg_str, quote_char); + m_argv[idx] = m_entries[idx].data(); + } else { + const char *src_data = arg_str.data() ? arg_str.data() : ""; + ::memcpy(m_entries[idx].data(), src_data, arg_str.size()); + m_entries[idx].ptr[arg_str.size()] = 0; + m_entries[idx].ref = m_entries[idx].ref.take_front(arg_str.size()); + } +} + +void Args::DeleteArgumentAtIndex(size_t idx) { + if (idx >= m_entries.size()) + return; + + m_argv.erase(m_argv.begin() + idx); + m_entries.erase(m_entries.begin() + idx); +} + +void Args::SetArguments(size_t argc, const char **argv) { + Clear(); + + auto args = llvm::makeArrayRef(argv, argc); + m_entries.resize(argc); + m_argv.resize(argc + 1); + for (size_t i = 0; i < args.size(); ++i) { + char quote = + ((args[i][0] == '\'') || (args[i][0] == '"') || (args[i][0] == '`')) + ? args[i][0] + : '\0'; + + m_entries[i] = ArgEntry(args[i], quote); + m_argv[i] = m_entries[i].data(); + } +} + +void Args::SetArguments(const char **argv) { + SetArguments(ArgvToArgc(argv), argv); +} + +void Args::Clear() { + m_entries.clear(); + m_argv.clear(); + m_argv.push_back(nullptr); +} + +const char *Args::StripSpaces(std::string &s, bool leading, bool trailing, + bool return_null_if_empty) { + static const char *k_white_space = " \t\v"; + if (!s.empty()) { + if (leading) { + size_t pos = s.find_first_not_of(k_white_space); + if (pos == std::string::npos) + s.clear(); + else if (pos > 0) + s.erase(0, pos); + } + + if (trailing) { + size_t rpos = s.find_last_not_of(k_white_space); + if (rpos != std::string::npos && rpos + 1 < s.size()) + s.erase(rpos + 1); + } + } + if (return_null_if_empty && s.empty()) + return nullptr; + return s.c_str(); +} + +bool Args::StringToVersion(llvm::StringRef string, uint32_t &major, + uint32_t &minor, uint32_t &update) { + major = UINT32_MAX; + minor = UINT32_MAX; + update = UINT32_MAX; + + if (string.empty()) + return false; + + llvm::StringRef major_str, minor_str, update_str; + + std::tie(major_str, minor_str) = string.split('.'); + std::tie(minor_str, update_str) = minor_str.split('.'); + if (major_str.getAsInteger(10, major)) + return false; + if (!minor_str.empty() && minor_str.getAsInteger(10, minor)) + return false; + if (!update_str.empty() && update_str.getAsInteger(10, update)) + return false; + + return true; +} + +const char *Args::GetShellSafeArgument(const FileSpec &shell, + const char *unsafe_arg, + std::string &safe_arg) { + struct ShellDescriptor { + ConstString m_basename; + const char *m_escapables; + }; + + static ShellDescriptor g_Shells[] = {{ConstString("bash"), " '\"<>()&"}, + {ConstString("tcsh"), " '\"<>()&$"}, + {ConstString("sh"), " '\"<>()&"}}; + + // safe minimal set + const char *escapables = " '\""; + + if (auto basename = shell.GetFilename()) { + for (const auto &Shell : g_Shells) { + if (Shell.m_basename == basename) { + escapables = Shell.m_escapables; + break; + } + } + } + + 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(escapables, prev_pos); + if (pos != std::string::npos) { + safe_arg.insert(pos, 1, '\\'); + prev_pos = pos + 2; + } else + break; + } + return safe_arg.c_str(); +} + +lldb::Encoding Args::StringToEncoding(llvm::StringRef s, + lldb::Encoding fail_value) { + return llvm::StringSwitch(s) + .Case("uint", eEncodingUint) + .Case("sint", eEncodingSint) + .Case("ieee754", eEncodingIEEE754) + .Case("vector", eEncodingVector) + .Default(fail_value); +} + +uint32_t Args::StringToGenericRegister(llvm::StringRef s) { + if (s.empty()) + return LLDB_INVALID_REGNUM; + uint32_t result = llvm::StringSwitch(s) + .Case("pc", LLDB_REGNUM_GENERIC_PC) + .Case("sp", LLDB_REGNUM_GENERIC_SP) + .Case("fp", LLDB_REGNUM_GENERIC_FP) + .Cases("ra", "lr", LLDB_REGNUM_GENERIC_RA) + .Case("flags", LLDB_REGNUM_GENERIC_FLAGS) + .Case("arg1", LLDB_REGNUM_GENERIC_ARG1) + .Case("arg2", LLDB_REGNUM_GENERIC_ARG2) + .Case("arg3", LLDB_REGNUM_GENERIC_ARG3) + .Case("arg4", LLDB_REGNUM_GENERIC_ARG4) + .Case("arg5", LLDB_REGNUM_GENERIC_ARG5) + .Case("arg6", LLDB_REGNUM_GENERIC_ARG6) + .Case("arg7", LLDB_REGNUM_GENERIC_ARG7) + .Case("arg8", LLDB_REGNUM_GENERIC_ARG8) + .Default(LLDB_INVALID_REGNUM); + return result; +} + +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, nullptr, 8); + if (octal_value <= UINT8_MAX) { + dst.append(1, (char)octal_value); + } + } + 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, nullptr, 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 (isprint(*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; + } + } + } + } +} + +std::string Args::EscapeLLDBCommandArgument(const std::string &arg, + char quote_char) { + const char *chars_to_escape = nullptr; + switch (quote_char) { + case '\0': + chars_to_escape = " \t\\'\"`"; + break; + case '\'': + chars_to_escape = ""; + break; + case '"': + chars_to_escape = "$\"`\\"; + break; + default: + assert(false && "Unhandled quote character"); + } + + std::string res; + res.reserve(arg.size()); + for (char c : arg) { + if (::strchr(chars_to_escape, c)) + res.push_back('\\'); + res.push_back(c); + } + return res; +} Index: lldb/trunk/source/Utility/CMakeLists.txt =================================================================== --- lldb/trunk/source/Utility/CMakeLists.txt +++ lldb/trunk/source/Utility/CMakeLists.txt @@ -41,6 +41,7 @@ add_lldb_library(lldbUtility ArchSpec.cpp + Args.cpp Baton.cpp Connection.cpp ConstString.cpp Index: lldb/trunk/tools/lldb-server/LLDBServerUtilities.cpp =================================================================== --- lldb/trunk/tools/lldb-server/LLDBServerUtilities.cpp +++ lldb/trunk/tools/lldb-server/LLDBServerUtilities.cpp @@ -10,7 +10,7 @@ #include "LLDBServerUtilities.h" #include "lldb/Core/StreamFile.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" Index: lldb/trunk/unittests/Interpreter/CMakeLists.txt =================================================================== --- lldb/trunk/unittests/Interpreter/CMakeLists.txt +++ lldb/trunk/unittests/Interpreter/CMakeLists.txt @@ -1,5 +1,4 @@ add_lldb_unittest(InterpreterTests - TestArgs.cpp TestCompletion.cpp TestOptionArgParser.cpp Index: lldb/trunk/unittests/Interpreter/TestArgs.cpp =================================================================== --- lldb/trunk/unittests/Interpreter/TestArgs.cpp +++ lldb/trunk/unittests/Interpreter/TestArgs.cpp @@ -1,189 +0,0 @@ -//===-- ArgsTest.cpp --------------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "gtest/gtest.h" - -#include "lldb/Interpreter/Args.h" -#include "lldb/Utility/StringList.h" - -#include -#include - -using namespace lldb_private; - -TEST(ArgsTest, TestSingleArg) { - Args args; - args.SetCommandString("arg"); - EXPECT_EQ(1u, args.GetArgumentCount()); - EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg"); -} - -TEST(ArgsTest, TestSingleQuotedArgWithSpace) { - Args args; - args.SetCommandString("\"arg with space\""); - EXPECT_EQ(1u, args.GetArgumentCount()); - EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space"); -} - -TEST(ArgsTest, TestSingleArgWithQuotedSpace) { - Args args; - args.SetCommandString("arg\\ with\\ space"); - EXPECT_EQ(1u, args.GetArgumentCount()); - EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space"); -} - -TEST(ArgsTest, TestMultipleArgs) { - Args args; - args.SetCommandString("this has multiple args"); - EXPECT_EQ(4u, args.GetArgumentCount()); - EXPECT_STREQ(args.GetArgumentAtIndex(0), "this"); - EXPECT_STREQ(args.GetArgumentAtIndex(1), "has"); - EXPECT_STREQ(args.GetArgumentAtIndex(2), "multiple"); - EXPECT_STREQ(args.GetArgumentAtIndex(3), "args"); -} - -TEST(ArgsTest, TestOverwriteArgs) { - Args args; - args.SetCommandString("this has multiple args"); - EXPECT_EQ(4u, args.GetArgumentCount()); - args.SetCommandString("arg"); - EXPECT_EQ(1u, args.GetArgumentCount()); - EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg"); -} - -TEST(ArgsTest, TestAppendArg) { - Args args; - args.SetCommandString("first_arg"); - EXPECT_EQ(1u, args.GetArgumentCount()); - args.AppendArgument(llvm::StringRef("second_arg")); - EXPECT_EQ(2u, args.GetArgumentCount()); - EXPECT_STREQ(args.GetArgumentAtIndex(0), "first_arg"); - EXPECT_STREQ(args.GetArgumentAtIndex(1), "second_arg"); -} - -TEST(ArgsTest, TestInsertArg) { - Args args; - args.AppendArgument("1"); - args.AppendArgument("2"); - args.AppendArgument("3"); - args.InsertArgumentAtIndex(1, "1.5"); - args.InsertArgumentAtIndex(4, "3.5"); - - ASSERT_EQ(5u, args.GetArgumentCount()); - EXPECT_STREQ("1", args.GetArgumentAtIndex(0)); - EXPECT_STREQ("1.5", args.GetArgumentAtIndex(1)); - EXPECT_STREQ("2", args.GetArgumentAtIndex(2)); - EXPECT_STREQ("3", args.GetArgumentAtIndex(3)); - EXPECT_STREQ("3.5", args.GetArgumentAtIndex(4)); -} - -TEST(ArgsTest, TestArgv) { - Args args; - EXPECT_EQ(nullptr, args.GetArgumentVector()); - - args.AppendArgument("1"); - EXPECT_NE(nullptr, args.GetArgumentVector()[0]); - EXPECT_EQ(nullptr, args.GetArgumentVector()[1]); - - args.AppendArgument("2"); - EXPECT_NE(nullptr, args.GetArgumentVector()[0]); - EXPECT_NE(nullptr, args.GetArgumentVector()[1]); - EXPECT_EQ(nullptr, args.GetArgumentVector()[2]); - - args.AppendArgument("3"); - EXPECT_NE(nullptr, args.GetArgumentVector()[0]); - EXPECT_NE(nullptr, args.GetArgumentVector()[1]); - EXPECT_NE(nullptr, args.GetArgumentVector()[2]); - EXPECT_EQ(nullptr, args.GetArgumentVector()[3]); - - args.InsertArgumentAtIndex(1, "1.5"); - EXPECT_NE(nullptr, args.GetArgumentVector()[0]); - EXPECT_NE(nullptr, args.GetArgumentVector()[1]); - EXPECT_NE(nullptr, args.GetArgumentVector()[2]); - EXPECT_NE(nullptr, args.GetArgumentVector()[3]); - EXPECT_EQ(nullptr, args.GetArgumentVector()[4]); - - args.InsertArgumentAtIndex(4, "3.5"); - EXPECT_NE(nullptr, args.GetArgumentVector()[0]); - EXPECT_NE(nullptr, args.GetArgumentVector()[1]); - EXPECT_NE(nullptr, args.GetArgumentVector()[2]); - EXPECT_NE(nullptr, args.GetArgumentVector()[3]); - EXPECT_NE(nullptr, args.GetArgumentVector()[4]); - EXPECT_EQ(nullptr, args.GetArgumentVector()[5]); -} - -TEST(ArgsTest, StringListConstructor) { - StringList list; - list << "foo" << "bar" << "baz"; - Args args(list); - ASSERT_EQ(3u, args.GetArgumentCount()); - EXPECT_EQ("foo", args[0].ref); - EXPECT_EQ("bar", args[1].ref); - EXPECT_EQ("baz", args[2].ref); -} - -TEST(ArgsTest, GetQuotedCommandString) { - Args args; - const char *str = "process launch -o stdout.txt -- \"a b c\""; - args.SetCommandString(str); - - std::string stdstr; - ASSERT_TRUE(args.GetQuotedCommandString(stdstr)); - EXPECT_EQ(str, stdstr); -} - -TEST(ArgsTest, BareSingleQuote) { - Args args; - args.SetCommandString("a\\'b"); - EXPECT_EQ(1u, args.GetArgumentCount()); - - EXPECT_STREQ("a'b", args.GetArgumentAtIndex(0)); -} - -TEST(ArgsTest, DoubleQuotedItem) { - Args args; - args.SetCommandString("\"a b c\""); - EXPECT_EQ(1u, args.GetArgumentCount()); - - EXPECT_STREQ("a b c", args.GetArgumentAtIndex(0)); -} - -TEST(ArgsTest, AppendArguments) { - Args args; - const char *argv[] = {"1", "2", nullptr}; - const char *argv2[] = {"3", "4", nullptr}; - - args.AppendArguments(argv); - ASSERT_EQ(2u, args.GetArgumentCount()); - EXPECT_STREQ("1", args.GetArgumentVector()[0]); - EXPECT_STREQ("2", args.GetArgumentVector()[1]); - EXPECT_EQ(nullptr, args.GetArgumentVector()[2]); - EXPECT_STREQ("1", args.GetArgumentAtIndex(0)); - EXPECT_STREQ("2", args.GetArgumentAtIndex(1)); - - args.AppendArguments(argv2); - ASSERT_EQ(4u, args.GetArgumentCount()); - EXPECT_STREQ("1", args.GetArgumentVector()[0]); - EXPECT_STREQ("2", args.GetArgumentVector()[1]); - EXPECT_STREQ("3", args.GetArgumentVector()[2]); - EXPECT_STREQ("4", args.GetArgumentVector()[3]); - EXPECT_EQ(nullptr, args.GetArgumentVector()[4]); - EXPECT_STREQ("1", args.GetArgumentAtIndex(0)); - EXPECT_STREQ("2", args.GetArgumentAtIndex(1)); - EXPECT_STREQ("3", args.GetArgumentAtIndex(2)); - EXPECT_STREQ("4", args.GetArgumentAtIndex(3)); -} - -TEST(ArgsTest, GetArgumentArrayRef) { - Args args("foo bar"); - auto ref = args.GetArgumentArrayRef(); - ASSERT_EQ(2u, ref.size()); - EXPECT_STREQ("foo", ref[0]); - EXPECT_STREQ("bar", ref[1]); -} Index: lldb/trunk/unittests/Utility/ArgsTest.cpp =================================================================== --- lldb/trunk/unittests/Utility/ArgsTest.cpp +++ lldb/trunk/unittests/Utility/ArgsTest.cpp @@ -0,0 +1,191 @@ +//===-- ArgsTest.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Utility/Args.h" +#include "lldb/Utility/StringList.h" + +#include +#include + +using namespace lldb_private; + +TEST(ArgsTest, TestSingleArg) { + Args args; + args.SetCommandString("arg"); + EXPECT_EQ(1u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg"); +} + +TEST(ArgsTest, TestSingleQuotedArgWithSpace) { + Args args; + args.SetCommandString("\"arg with space\""); + EXPECT_EQ(1u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space"); +} + +TEST(ArgsTest, TestSingleArgWithQuotedSpace) { + Args args; + args.SetCommandString("arg\\ with\\ space"); + EXPECT_EQ(1u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space"); +} + +TEST(ArgsTest, TestMultipleArgs) { + Args args; + args.SetCommandString("this has multiple args"); + EXPECT_EQ(4u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "this"); + EXPECT_STREQ(args.GetArgumentAtIndex(1), "has"); + EXPECT_STREQ(args.GetArgumentAtIndex(2), "multiple"); + EXPECT_STREQ(args.GetArgumentAtIndex(3), "args"); +} + +TEST(ArgsTest, TestOverwriteArgs) { + Args args; + args.SetCommandString("this has multiple args"); + EXPECT_EQ(4u, args.GetArgumentCount()); + args.SetCommandString("arg"); + EXPECT_EQ(1u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg"); +} + +TEST(ArgsTest, TestAppendArg) { + Args args; + args.SetCommandString("first_arg"); + EXPECT_EQ(1u, args.GetArgumentCount()); + args.AppendArgument(llvm::StringRef("second_arg")); + EXPECT_EQ(2u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "first_arg"); + EXPECT_STREQ(args.GetArgumentAtIndex(1), "second_arg"); +} + +TEST(ArgsTest, TestInsertArg) { + Args args; + args.AppendArgument("1"); + args.AppendArgument("2"); + args.AppendArgument("3"); + args.InsertArgumentAtIndex(1, "1.5"); + args.InsertArgumentAtIndex(4, "3.5"); + + ASSERT_EQ(5u, args.GetArgumentCount()); + EXPECT_STREQ("1", args.GetArgumentAtIndex(0)); + EXPECT_STREQ("1.5", args.GetArgumentAtIndex(1)); + EXPECT_STREQ("2", args.GetArgumentAtIndex(2)); + EXPECT_STREQ("3", args.GetArgumentAtIndex(3)); + EXPECT_STREQ("3.5", args.GetArgumentAtIndex(4)); +} + +TEST(ArgsTest, TestArgv) { + Args args; + EXPECT_EQ(nullptr, args.GetArgumentVector()); + + args.AppendArgument("1"); + EXPECT_NE(nullptr, args.GetArgumentVector()[0]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[1]); + + args.AppendArgument("2"); + EXPECT_NE(nullptr, args.GetArgumentVector()[0]); + EXPECT_NE(nullptr, args.GetArgumentVector()[1]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[2]); + + args.AppendArgument("3"); + EXPECT_NE(nullptr, args.GetArgumentVector()[0]); + EXPECT_NE(nullptr, args.GetArgumentVector()[1]); + EXPECT_NE(nullptr, args.GetArgumentVector()[2]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[3]); + + args.InsertArgumentAtIndex(1, "1.5"); + EXPECT_NE(nullptr, args.GetArgumentVector()[0]); + EXPECT_NE(nullptr, args.GetArgumentVector()[1]); + EXPECT_NE(nullptr, args.GetArgumentVector()[2]); + EXPECT_NE(nullptr, args.GetArgumentVector()[3]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[4]); + + args.InsertArgumentAtIndex(4, "3.5"); + EXPECT_NE(nullptr, args.GetArgumentVector()[0]); + EXPECT_NE(nullptr, args.GetArgumentVector()[1]); + EXPECT_NE(nullptr, args.GetArgumentVector()[2]); + EXPECT_NE(nullptr, args.GetArgumentVector()[3]); + EXPECT_NE(nullptr, args.GetArgumentVector()[4]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[5]); +} + +TEST(ArgsTest, StringListConstructor) { + StringList list; + list << "foo" + << "bar" + << "baz"; + Args args(list); + ASSERT_EQ(3u, args.GetArgumentCount()); + EXPECT_EQ("foo", args[0].ref); + EXPECT_EQ("bar", args[1].ref); + EXPECT_EQ("baz", args[2].ref); +} + +TEST(ArgsTest, GetQuotedCommandString) { + Args args; + const char *str = "process launch -o stdout.txt -- \"a b c\""; + args.SetCommandString(str); + + std::string stdstr; + ASSERT_TRUE(args.GetQuotedCommandString(stdstr)); + EXPECT_EQ(str, stdstr); +} + +TEST(ArgsTest, BareSingleQuote) { + Args args; + args.SetCommandString("a\\'b"); + EXPECT_EQ(1u, args.GetArgumentCount()); + + EXPECT_STREQ("a'b", args.GetArgumentAtIndex(0)); +} + +TEST(ArgsTest, DoubleQuotedItem) { + Args args; + args.SetCommandString("\"a b c\""); + EXPECT_EQ(1u, args.GetArgumentCount()); + + EXPECT_STREQ("a b c", args.GetArgumentAtIndex(0)); +} + +TEST(ArgsTest, AppendArguments) { + Args args; + const char *argv[] = {"1", "2", nullptr}; + const char *argv2[] = {"3", "4", nullptr}; + + args.AppendArguments(argv); + ASSERT_EQ(2u, args.GetArgumentCount()); + EXPECT_STREQ("1", args.GetArgumentVector()[0]); + EXPECT_STREQ("2", args.GetArgumentVector()[1]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[2]); + EXPECT_STREQ("1", args.GetArgumentAtIndex(0)); + EXPECT_STREQ("2", args.GetArgumentAtIndex(1)); + + args.AppendArguments(argv2); + ASSERT_EQ(4u, args.GetArgumentCount()); + EXPECT_STREQ("1", args.GetArgumentVector()[0]); + EXPECT_STREQ("2", args.GetArgumentVector()[1]); + EXPECT_STREQ("3", args.GetArgumentVector()[2]); + EXPECT_STREQ("4", args.GetArgumentVector()[3]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[4]); + EXPECT_STREQ("1", args.GetArgumentAtIndex(0)); + EXPECT_STREQ("2", args.GetArgumentAtIndex(1)); + EXPECT_STREQ("3", args.GetArgumentAtIndex(2)); + EXPECT_STREQ("4", args.GetArgumentAtIndex(3)); +} + +TEST(ArgsTest, GetArgumentArrayRef) { + Args args("foo bar"); + auto ref = args.GetArgumentArrayRef(); + ASSERT_EQ(2u, ref.size()); + EXPECT_STREQ("foo", ref[0]); + EXPECT_STREQ("bar", ref[1]); +} Index: lldb/trunk/unittests/Utility/CMakeLists.txt =================================================================== --- lldb/trunk/unittests/Utility/CMakeLists.txt +++ lldb/trunk/unittests/Utility/CMakeLists.txt @@ -1,4 +1,5 @@ add_lldb_unittest(UtilityTests + ArgsTest.cpp ArchSpecTest.cpp CleanUpTest.cpp ConstStringTest.cpp Index: lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.cpp =================================================================== --- lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.cpp +++ lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// #include "MessageObjects.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/StringExtractor.h" #include "llvm/ADT/StringExtras.h" #include "gtest/gtest.h" Index: lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp =================================================================== --- lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp +++ lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp @@ -11,8 +11,8 @@ #include "lldb/Host/HostInfo.h" #include "lldb/Host/common/TCPSocket.h" #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Utility/Args.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Path.h" #include "llvm/Testing/Support/Error.h"