diff options
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Commands/CommandCompletions.cpp')
| -rw-r--r-- | contrib/llvm/tools/lldb/source/Commands/CommandCompletions.cpp | 754 | 
1 files changed, 754 insertions, 0 deletions
diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandCompletions.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandCompletions.cpp new file mode 100644 index 000000000000..a9d2f21b9bac --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandCompletions.cpp @@ -0,0 +1,754 @@ +//===-- CommandCompletions.cpp ----------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include <sys/stat.h> +#include <dirent.h> +#if defined(__APPLE__) || defined(__linux__) +#include <pwd.h> +#endif + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Module.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/CleanUp.h" + +using namespace lldb_private; + +CommandCompletions::CommonCompletionElement +CommandCompletions::g_common_completions[] = +{ +    {eCustomCompletion,          NULL}, +    {eSourceFileCompletion,      CommandCompletions::SourceFiles}, +    {eDiskFileCompletion,        CommandCompletions::DiskFiles}, +    {eDiskDirectoryCompletion,   CommandCompletions::DiskDirectories}, +    {eSymbolCompletion,          CommandCompletions::Symbols}, +    {eModuleCompletion,          CommandCompletions::Modules}, +    {eSettingsNameCompletion,    CommandCompletions::SettingsNames}, +    {ePlatformPluginCompletion,  CommandCompletions::PlatformPluginNames}, +    {eArchitectureCompletion,    CommandCompletions::ArchitectureNames}, +    {eVariablePathCompletion,    CommandCompletions::VariablePath}, +    {eNoCompletion,              NULL}      // This one has to be last in the list. +}; + +bool +CommandCompletions::InvokeCommonCompletionCallbacks  +( +    CommandInterpreter &interpreter, +    uint32_t completion_mask, +    const char *completion_str, +    int match_start_point, +    int max_return_elements, +    SearchFilter *searcher, +    bool &word_complete, +    StringList &matches +) +{ +    bool handled = false; + +    if (completion_mask & eCustomCompletion) +        return false; + +    for (int i = 0; ; i++) +    { +        if (g_common_completions[i].type == eNoCompletion) +            break; +         else if ((g_common_completions[i].type & completion_mask) == g_common_completions[i].type +                   && g_common_completions[i].callback != NULL) +         { +            handled = true; +            g_common_completions[i].callback (interpreter, +                                              completion_str, +                                              match_start_point, +                                              max_return_elements, +                                              searcher, +                                              word_complete, +                                              matches); +        } +    } +    return handled; +} + +int +CommandCompletions::SourceFiles  +( +    CommandInterpreter &interpreter, +    const char *partial_file_name, +    int match_start_point, +    int max_return_elements, +    SearchFilter *searcher, +    bool &word_complete, +    StringList &matches +) +{ +    word_complete = true; +    // Find some way to switch "include support files..." +    SourceFileCompleter completer (interpreter, +                                   false,  +                                   partial_file_name,  +                                   match_start_point,  +                                   max_return_elements, +                                   matches); + +    if (searcher == NULL) +    { +        lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); +        SearchFilter null_searcher (target_sp); +        completer.DoCompletion (&null_searcher); +    } +    else +    { +        completer.DoCompletion (searcher); +    } +    return matches.GetSize(); +} + +static int +DiskFilesOrDirectories  +( +    const char *partial_file_name, +    bool only_directories, +    bool &saw_directory, +    StringList &matches +) +{ +    // I'm going to  use the "glob" function with GLOB_TILDE for user directory expansion.   +    // If it is not defined on your host system, you'll need to implement it yourself... +     +    size_t partial_name_len = strlen(partial_file_name); +     +    if (partial_name_len >= PATH_MAX) +        return matches.GetSize(); +     +    // This copy of the string will be cut up into the directory part, and the remainder.  end_ptr +    // below will point to the place of the remainder in this string.  Then when we've resolved the +    // containing directory, and opened it, we'll read the directory contents and overwrite the +    // partial_name_copy starting from end_ptr with each of the matches.  Thus we will preserve +    // the form the user originally typed. +     +    char partial_name_copy[PATH_MAX]; +    memcpy(partial_name_copy, partial_file_name, partial_name_len); +    partial_name_copy[partial_name_len] = '\0'; +     +    // We'll need to save a copy of the remainder for comparison, which we do here. +    char remainder[PATH_MAX]; +     +    // end_ptr will point past the last / in partial_name_copy, or if there is no slash to the beginning of the string. +    char *end_ptr; +     +    end_ptr = strrchr(partial_name_copy, '/'); +     +    // This will store the resolved form of the containing directory +    char containing_part[PATH_MAX]; +     +    if (end_ptr == NULL) +    { +        // There's no directory.  If the thing begins with a "~" then this is a bare +        // user name. +        if (*partial_name_copy == '~') +        { +            // Nothing here but the user name.  We could just put a slash on the end,  +            // but for completeness sake we'll resolve the user name and only put a slash +            // on the end if it exists. +            char resolved_username[PATH_MAX]; +            size_t resolved_username_len = FileSpec::ResolveUsername (partial_name_copy, resolved_username,  +                                                          sizeof (resolved_username)); +                                                           +           // Not sure how this would happen, a username longer than PATH_MAX?  Still... +            if (resolved_username_len >= sizeof (resolved_username)) +                return matches.GetSize(); +            else if (resolved_username_len == 0) +            { +                // The user name didn't resolve, let's look in the password database for matches. +                // The user name database contains duplicates, and is not in alphabetical order, so +                // we'll use a set to manage that for us. +                FileSpec::ResolvePartialUsername (partial_name_copy, matches); +                if (matches.GetSize() > 0) +                    saw_directory = true; +                return matches.GetSize(); +            }  +            else +            {    +                //The thing exists, put a '/' on the end, and return it... +                // FIXME: complete user names here: +                partial_name_copy[partial_name_len] = '/'; +                partial_name_copy[partial_name_len+1] = '\0'; +                matches.AppendString(partial_name_copy); +                saw_directory = true; +                return matches.GetSize(); +            } +        } +        else +        { +            // The containing part is the CWD, and the whole string is the remainder. +            containing_part[0] = '.'; +            containing_part[1] = '\0'; +            strcpy(remainder, partial_name_copy); +            end_ptr = partial_name_copy; +        } +    } +    else +    { +        if (end_ptr == partial_name_copy) +        { +            // We're completing a file or directory in the root volume. +            containing_part[0] = '/'; +            containing_part[1] = '\0'; +        } +        else +        { +            size_t len = end_ptr - partial_name_copy; +            memcpy(containing_part, partial_name_copy, len); +            containing_part[len] = '\0'; +        } +        // Push end_ptr past the final "/" and set remainder. +        end_ptr++; +        strcpy(remainder, end_ptr); +    } +     +    // Look for a user name in the containing part, and if it's there, resolve it and stick the +    // result back into the containing_part: + +    if (*partial_name_copy == '~') +    { +        size_t resolved_username_len = FileSpec::ResolveUsername(containing_part,  +                                                                 containing_part,  +                                                                 sizeof (containing_part)); +        // User name doesn't exist, we're not getting any further... +        if (resolved_username_len == 0 || resolved_username_len >= sizeof (containing_part)) +            return matches.GetSize(); +    } + +    // Okay, containing_part is now the directory we want to open and look for files: + +    lldb_utility::CleanUp <DIR *, int> dir_stream (opendir(containing_part), NULL, closedir); +    if (!dir_stream.is_valid()) +        return matches.GetSize(); +     +    struct dirent *dirent_buf; +     +    size_t baselen = end_ptr - partial_name_copy; +     +    while ((dirent_buf = readdir(dir_stream.get())) != NULL)  +    { +        char *name = dirent_buf->d_name; +         +        // Omit ".", ".." and any . files if the match string doesn't start with . +        if (name[0] == '.') +        { +            if (name[1] == '\0') +                continue; +            else if (name[1] == '.' && name[2] == '\0') +                continue; +            else if (remainder[0] != '.') +                continue; +        } +         +        // If we found a directory, we put a "/" at the end of the name. +         +        if (remainder[0] == '\0' || strstr(dirent_buf->d_name, remainder) == name) +        { +            if (strlen(name) + baselen >= PATH_MAX) +                continue; +                 +            strcpy(end_ptr, name); +             +            bool isa_directory = false; +            if (dirent_buf->d_type & DT_DIR) +                isa_directory = true; +            else if (dirent_buf->d_type & DT_LNK) +            {  +                struct stat stat_buf; +                if ((stat(partial_name_copy, &stat_buf) == 0) && S_ISDIR(stat_buf.st_mode)) +                    isa_directory = true; +            } +             +            if (isa_directory) +            { +                saw_directory = true; +                size_t len = strlen(partial_name_copy); +                partial_name_copy[len] = '/'; +                partial_name_copy[len + 1] = '\0'; +            } +            if (only_directories && !isa_directory) +                continue; +            matches.AppendString(partial_name_copy); +        } +    } +     +    return matches.GetSize(); +} + +int +CommandCompletions::DiskFiles  +( +    CommandInterpreter &interpreter, +    const char *partial_file_name, +    int match_start_point, +    int max_return_elements, +    SearchFilter *searcher, +    bool &word_complete, +    StringList &matches +) +{ + +    int ret_val = DiskFilesOrDirectories (partial_file_name, +                                          false, +                                          word_complete, +                                          matches); +    word_complete = !word_complete; +    return ret_val; +} + +int +CommandCompletions::DiskDirectories  +( +    CommandInterpreter &interpreter, +    const char *partial_file_name, +    int match_start_point, +    int max_return_elements, +    SearchFilter *searcher, +    bool &word_complete, +    StringList &matches +) +{ +    int ret_val =  DiskFilesOrDirectories (partial_file_name, +                                           true, +                                           word_complete, +                                           matches);  +    word_complete = false; +    return ret_val; +} + +int +CommandCompletions::Modules  +( +    CommandInterpreter &interpreter, +    const char *partial_file_name, +    int match_start_point, +    int max_return_elements, +    SearchFilter *searcher, +    bool &word_complete, +    StringList &matches +) +{ +    word_complete = true; +    ModuleCompleter completer (interpreter, +                               partial_file_name,  +                               match_start_point,  +                               max_return_elements,  +                               matches); +     +    if (searcher == NULL) +    { +        lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); +        SearchFilter null_searcher (target_sp); +        completer.DoCompletion (&null_searcher); +    } +    else +    { +        completer.DoCompletion (searcher); +    } +    return matches.GetSize(); +} + +int +CommandCompletions::Symbols  +( +    CommandInterpreter &interpreter, +    const char *partial_file_name, +    int match_start_point, +    int max_return_elements, +    SearchFilter *searcher, +    bool &word_complete, +    StringList &matches) +{ +    word_complete = true; +    SymbolCompleter completer (interpreter, +                               partial_file_name,  +                               match_start_point,  +                               max_return_elements,  +                               matches); + +    if (searcher == NULL) +    { +        lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); +        SearchFilter null_searcher (target_sp); +        completer.DoCompletion (&null_searcher); +    } +    else +    { +        completer.DoCompletion (searcher); +    } +    return matches.GetSize(); +} + +int +CommandCompletions::SettingsNames (CommandInterpreter &interpreter, +                                   const char *partial_setting_name, +                                   int match_start_point, +                                   int max_return_elements, +                                   SearchFilter *searcher, +                                   bool &word_complete, +                                   StringList &matches) +{ +    // Cache the full setting name list +    static StringList g_property_names; +    if (g_property_names.GetSize() == 0) +    { +        // Generate the full setting name list on demand +        lldb::OptionValuePropertiesSP properties_sp (interpreter.GetDebugger().GetValueProperties()); +        if (properties_sp) +        { +            StreamString strm; +            properties_sp->DumpValue(NULL, strm, OptionValue::eDumpOptionName); +            const std::string &str = strm.GetString(); +            g_property_names.SplitIntoLines(str.c_str(), str.size()); +        } +    } +     +    size_t exact_matches_idx = SIZE_MAX; +    const size_t num_matches = g_property_names.AutoComplete (partial_setting_name, matches, exact_matches_idx); +    word_complete = exact_matches_idx != SIZE_MAX; +    return num_matches; +} + + +int +CommandCompletions::PlatformPluginNames (CommandInterpreter &interpreter, +                                         const char *partial_name, +                                         int match_start_point, +                                         int max_return_elements, +                                         SearchFilter *searcher, +                                         bool &word_complete, +                                         lldb_private::StringList &matches) +{ +    const uint32_t num_matches = PluginManager::AutoCompletePlatformName(partial_name, matches); +    word_complete = num_matches == 1; +    return num_matches; +} + +int +CommandCompletions::ArchitectureNames (CommandInterpreter &interpreter, +                                       const char *partial_name, +                                       int match_start_point, +                                       int max_return_elements, +                                       SearchFilter *searcher, +                                       bool &word_complete, +                                       lldb_private::StringList &matches) +{ +    const uint32_t num_matches = ArchSpec::AutoComplete (partial_name, matches); +    word_complete = num_matches == 1; +    return num_matches; +} + + +int +CommandCompletions::VariablePath (CommandInterpreter &interpreter, +                                  const char *partial_name, +                                  int match_start_point, +                                  int max_return_elements, +                                  SearchFilter *searcher, +                                  bool &word_complete, +                                  lldb_private::StringList &matches) +{ +    return Variable::AutoComplete (interpreter.GetExecutionContext(), partial_name, matches, word_complete); +} + + +CommandCompletions::Completer::Completer  +( +    CommandInterpreter &interpreter, +    const char *completion_str, +    int match_start_point, +    int max_return_elements, +    StringList &matches +) : +    m_interpreter (interpreter), +    m_completion_str (completion_str), +    m_match_start_point (match_start_point), +    m_max_return_elements (max_return_elements), +    m_matches (matches) +{ +} + +CommandCompletions::Completer::~Completer () +{ + +} + +//---------------------------------------------------------------------- +// SourceFileCompleter +//---------------------------------------------------------------------- + +CommandCompletions::SourceFileCompleter::SourceFileCompleter  +( +    CommandInterpreter &interpreter, +    bool include_support_files, +    const char *completion_str, +    int match_start_point, +    int max_return_elements, +    StringList &matches +) : +    CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches), +    m_include_support_files (include_support_files), +    m_matching_files() +{ +    FileSpec partial_spec (m_completion_str.c_str(), false); +    m_file_name = partial_spec.GetFilename().GetCString(); +    m_dir_name = partial_spec.GetDirectory().GetCString(); +} + +Searcher::Depth +CommandCompletions::SourceFileCompleter::GetDepth() +{ +    return eDepthCompUnit; +} + +Searcher::CallbackReturn +CommandCompletions::SourceFileCompleter::SearchCallback ( +    SearchFilter &filter, +    SymbolContext &context, +    Address *addr, +    bool complete +) +{ +    if (context.comp_unit != NULL) +    { +        if (m_include_support_files) +        { +            FileSpecList supporting_files = context.comp_unit->GetSupportFiles(); +            for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) +            { +                const FileSpec &sfile_spec = supporting_files.GetFileSpecAtIndex(sfiles); +                const char *sfile_file_name = sfile_spec.GetFilename().GetCString(); +                const char *sfile_dir_name = sfile_spec.GetFilename().GetCString(); +                bool match = false; +                if (m_file_name && sfile_file_name +                    && strstr (sfile_file_name, m_file_name) == sfile_file_name) +                    match = true; +                if (match && m_dir_name && sfile_dir_name +                    && strstr (sfile_dir_name, m_dir_name) != sfile_dir_name) +                    match = false; + +                if (match) +                { +                    m_matching_files.AppendIfUnique(sfile_spec); +                } +            } + +        } +        else +        { +            const char *cur_file_name = context.comp_unit->GetFilename().GetCString(); +            const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString(); + +            bool match = false; +            if (m_file_name && cur_file_name +                && strstr (cur_file_name, m_file_name) == cur_file_name) +                match = true; + +            if (match && m_dir_name && cur_dir_name +                && strstr (cur_dir_name, m_dir_name) != cur_dir_name) +                match = false; + +            if (match) +            { +                m_matching_files.AppendIfUnique(context.comp_unit); +            } +        } +    } +    return Searcher::eCallbackReturnContinue; +} + +size_t +CommandCompletions::SourceFileCompleter::DoCompletion (SearchFilter *filter) +{ +    filter->Search (*this); +    // Now convert the filelist to completions: +    for (size_t i = 0; i < m_matching_files.GetSize(); i++) +    { +        m_matches.AppendString (m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); +    } +    return m_matches.GetSize(); + +} + +//---------------------------------------------------------------------- +// SymbolCompleter +//---------------------------------------------------------------------- + +static bool +regex_chars (const char comp) +{ +    if (comp == '[' || comp == ']' || +        comp == '(' || comp == ')' || +        comp == '{' || comp == '}' || +        comp == '+' || +        comp == '.' || +        comp == '*' || +        comp == '|' || +        comp == '^' || +        comp == '$' || +        comp == '\\' || +        comp == '?') +        return true; +    else +        return false; +} +CommandCompletions::SymbolCompleter::SymbolCompleter  +( +    CommandInterpreter &interpreter, +    const char *completion_str, +    int match_start_point, +    int max_return_elements, +    StringList &matches +) : +    CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches) +{ +    std::string regex_str; +    if (completion_str && completion_str[0]) +    { +        regex_str.append("^"); +        regex_str.append(completion_str); +    } +    else +    { +        // Match anything since the completion string is empty +        regex_str.append("."); +    } +    std::string::iterator pos = find_if(regex_str.begin() + 1, regex_str.end(), regex_chars); +    while (pos < regex_str.end()) +    { +        pos = regex_str.insert(pos, '\\'); +        pos = find_if(pos + 2, regex_str.end(), regex_chars); +    } +    m_regex.Compile(regex_str.c_str()); +} + +Searcher::Depth +CommandCompletions::SymbolCompleter::GetDepth() +{ +    return eDepthModule; +} + +Searcher::CallbackReturn +CommandCompletions::SymbolCompleter::SearchCallback ( +    SearchFilter &filter, +    SymbolContext &context, +    Address *addr, +    bool complete +) +{ +    if (context.module_sp) +    { +        SymbolContextList sc_list;         +        const bool include_symbols = true; +        const bool include_inlines = true; +        const bool append = true; +        context.module_sp->FindFunctions (m_regex, include_symbols, include_inlines, append, sc_list); + +        SymbolContext sc; +        // Now add the functions & symbols to the list - only add if unique: +        for (uint32_t i = 0; i < sc_list.GetSize(); i++) +        { +            if (sc_list.GetContextAtIndex(i, sc)) +            { +                ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled); +                if (!func_name.IsEmpty()) +                    m_match_set.insert (func_name); +            } +        } +    } +    return Searcher::eCallbackReturnContinue; +} + +size_t +CommandCompletions::SymbolCompleter::DoCompletion (SearchFilter *filter) +{ +    filter->Search (*this); +    collection::iterator pos = m_match_set.begin(), end = m_match_set.end(); +    for (pos = m_match_set.begin(); pos != end; pos++) +        m_matches.AppendString((*pos).GetCString()); + +    return m_matches.GetSize(); +} + +//---------------------------------------------------------------------- +// ModuleCompleter +//---------------------------------------------------------------------- +CommandCompletions::ModuleCompleter::ModuleCompleter  +( +    CommandInterpreter &interpreter, +    const char *completion_str, +    int match_start_point, +    int max_return_elements, +    StringList &matches +) : +    CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches) +{ +    FileSpec partial_spec (m_completion_str.c_str(), false); +    m_file_name = partial_spec.GetFilename().GetCString(); +    m_dir_name = partial_spec.GetDirectory().GetCString(); +} + +Searcher::Depth +CommandCompletions::ModuleCompleter::GetDepth() +{ +    return eDepthModule; +} + +Searcher::CallbackReturn +CommandCompletions::ModuleCompleter::SearchCallback ( +    SearchFilter &filter, +    SymbolContext &context, +    Address *addr, +    bool complete +) +{ +    if (context.module_sp) +    { +        const char *cur_file_name = context.module_sp->GetFileSpec().GetFilename().GetCString(); +        const char *cur_dir_name = context.module_sp->GetFileSpec().GetDirectory().GetCString(); + +        bool match = false; +        if (m_file_name && cur_file_name +            && strstr (cur_file_name, m_file_name) == cur_file_name) +            match = true; + +        if (match && m_dir_name && cur_dir_name +            && strstr (cur_dir_name, m_dir_name) != cur_dir_name) +            match = false; + +        if (match) +        { +            m_matches.AppendString (cur_file_name); +        } +    } +    return Searcher::eCallbackReturnContinue; +} + +size_t +CommandCompletions::ModuleCompleter::DoCompletion (SearchFilter *filter) +{ +    filter->Search (*this); +    return m_matches.GetSize(); +}  | 
