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 0000000..a9d2f21 --- /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(); +} |