//===-- 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 #include #include #include #include #include // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Core/FileSpecList.h" #include "lldb/Target/Target.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Core/FileSpec.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}, {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. setpwent(); struct passwd *user_entry; const char *name_start = partial_name_copy + 1; std::set name_list; while ((user_entry = getpwent()) != NULL) { if (strstr(user_entry->pw_name, name_start) == user_entry->pw_name) { std::string tmp_buf("~"); tmp_buf.append(user_entry->pw_name); tmp_buf.push_back('/'); name_list.insert(tmp_buf); saw_directory = true; } } std::set::iterator pos, end = name_list.end(); for (pos = name_list.begin(); pos != end; pos++) { matches.AppendString((*pos).c_str()); } return matches.GetSize(); } //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: DIR *dir_stream; dir_stream = opendir(containing_part); if (dir_stream == NULL) return matches.GetSize(); struct dirent *dirent_buf; size_t baselen = end_ptr - partial_name_copy; while ((dirent_buf = readdir(dir_stream)) != 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) && (stat_buf.st_mode & S_IFDIR)) 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(); } 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 append = true; context.module_sp->FindFunctions (m_regex, include_symbols, 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)) { if (sc.function) { m_match_set.insert (sc.function->GetMangled().GetDemangledName()); } else if (sc.symbol && sc.symbol->GetAddressRangePtr()) { m_match_set.insert (sc.symbol->GetMangled().GetDemangledName()); } } } } 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 != NULL) { 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(); }