llvm-project/lldb/source/Commands/CommandCompletions.cpp

711 lines
23 KiB
C++

//===-- CommandCompletions.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 <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/Interpreter/Args.h"
#include "lldb/Interpreter/CommandCompletions.h"
#include "lldb/Interpreter/CommandInterpreter.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},
{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...
int 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)
{
lldb::UserSettingsControllerSP root_settings = Debugger::GetSettingsController();
Args partial_setting_name_pieces = UserSettingsController::BreakNameIntoPieces (partial_setting_name);
return UserSettingsController::CompleteSettingsNames (root_settings,
partial_setting_name_pieces,
word_complete,
matches);
//return matches.GetSize();
}
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;
}
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 == ')')
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 ("^");
regex_str.append(completion_str);
regex_str.append(".*");
std::string::iterator pos;
pos = find_if(regex_str.begin(), regex_str.end(), regex_chars);
while (pos < regex_str.end()) {
pos = regex_str.insert(pos, '\\');
pos += 2;
pos = find_if(pos, 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();
}