1 //===-- CommandCompletions.cpp ----------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include <sys/stat.h> 11 #if defined(__APPLE__) || defined(__linux__) 12 #include <pwd.h> 13 #endif 14 15 #include "llvm/ADT/SmallString.h" 16 #include "llvm/ADT/StringSet.h" 17 18 #include "lldb/Core/FileSpecList.h" 19 #include "lldb/Core/Module.h" 20 #include "lldb/Core/PluginManager.h" 21 #include "lldb/Host/FileSystem.h" 22 #include "lldb/Interpreter/CommandCompletions.h" 23 #include "lldb/Interpreter/CommandInterpreter.h" 24 #include "lldb/Interpreter/OptionValueProperties.h" 25 #include "lldb/Symbol/CompileUnit.h" 26 #include "lldb/Symbol/Variable.h" 27 #include "lldb/Target/Target.h" 28 #include "lldb/Utility/Args.h" 29 #include "lldb/Utility/FileSpec.h" 30 #include "lldb/Utility/StreamString.h" 31 #include "lldb/Utility/TildeExpressionResolver.h" 32 33 #include "llvm/ADT/SmallString.h" 34 #include "llvm/Support/FileSystem.h" 35 #include "llvm/Support/Path.h" 36 37 using namespace lldb_private; 38 39 CommandCompletions::CommonCompletionElement 40 CommandCompletions::g_common_completions[] = { 41 {eCustomCompletion, nullptr}, 42 {eSourceFileCompletion, CommandCompletions::SourceFiles}, 43 {eDiskFileCompletion, CommandCompletions::DiskFiles}, 44 {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories}, 45 {eSymbolCompletion, CommandCompletions::Symbols}, 46 {eModuleCompletion, CommandCompletions::Modules}, 47 {eSettingsNameCompletion, CommandCompletions::SettingsNames}, 48 {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames}, 49 {eArchitectureCompletion, CommandCompletions::ArchitectureNames}, 50 {eVariablePathCompletion, CommandCompletions::VariablePath}, 51 {eNoCompletion, nullptr} // This one has to be last in the list. 52 }; 53 54 bool CommandCompletions::InvokeCommonCompletionCallbacks( 55 CommandInterpreter &interpreter, uint32_t completion_mask, 56 CompletionRequest &request, SearchFilter *searcher) { 57 bool handled = false; 58 59 if (completion_mask & eCustomCompletion) 60 return false; 61 62 for (int i = 0;; i++) { 63 if (g_common_completions[i].type == eNoCompletion) 64 break; 65 else if ((g_common_completions[i].type & completion_mask) == 66 g_common_completions[i].type && 67 g_common_completions[i].callback != nullptr) { 68 handled = true; 69 g_common_completions[i].callback(interpreter, request, searcher); 70 } 71 } 72 return handled; 73 } 74 75 int CommandCompletions::SourceFiles(CommandInterpreter &interpreter, 76 CompletionRequest &request, 77 SearchFilter *searcher) { 78 request.SetWordComplete(true); 79 // Find some way to switch "include support files..." 80 SourceFileCompleter completer(interpreter, false, request); 81 82 if (searcher == nullptr) { 83 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); 84 SearchFilterForUnconstrainedSearches null_searcher(target_sp); 85 completer.DoCompletion(&null_searcher); 86 } else { 87 completer.DoCompletion(searcher); 88 } 89 return request.GetNumberOfMatches(); 90 } 91 92 static int DiskFilesOrDirectories(const llvm::Twine &partial_name, 93 bool only_directories, StringList &matches, 94 TildeExpressionResolver &Resolver) { 95 matches.Clear(); 96 97 llvm::SmallString<256> CompletionBuffer; 98 llvm::SmallString<256> Storage; 99 partial_name.toVector(CompletionBuffer); 100 101 if (CompletionBuffer.size() >= PATH_MAX) 102 return matches.GetSize(); 103 104 namespace path = llvm::sys::path; 105 106 llvm::StringRef SearchDir; 107 llvm::StringRef PartialItem; 108 109 if (CompletionBuffer.startswith("~")) { 110 llvm::StringRef Buffer(CompletionBuffer); 111 size_t FirstSep = 112 Buffer.find_if([](char c) { return path::is_separator(c); }); 113 114 llvm::StringRef Username = Buffer.take_front(FirstSep); 115 llvm::StringRef Remainder; 116 if (FirstSep != llvm::StringRef::npos) 117 Remainder = Buffer.drop_front(FirstSep + 1); 118 119 llvm::SmallString<256> Resolved; 120 if (!Resolver.ResolveExact(Username, Resolved)) { 121 // We couldn't resolve it as a full username. If there were no slashes 122 // then this might be a partial username. We try to resolve it as such 123 // but after that, we're done regardless of any matches. 124 if (FirstSep == llvm::StringRef::npos) { 125 llvm::StringSet<> MatchSet; 126 Resolver.ResolvePartial(Username, MatchSet); 127 for (const auto &S : MatchSet) { 128 Resolved = S.getKey(); 129 path::append(Resolved, path::get_separator()); 130 matches.AppendString(Resolved); 131 } 132 } 133 return matches.GetSize(); 134 } 135 136 // If there was no trailing slash, then we're done as soon as we resolve 137 // the expression to the correct directory. Otherwise we need to continue 138 // looking for matches within that directory. 139 if (FirstSep == llvm::StringRef::npos) { 140 // Make sure it ends with a separator. 141 path::append(CompletionBuffer, path::get_separator()); 142 matches.AppendString(CompletionBuffer); 143 return matches.GetSize(); 144 } 145 146 // We want to keep the form the user typed, so we special case this to 147 // search in the fully resolved directory, but CompletionBuffer keeps the 148 // unmodified form that the user typed. 149 Storage = Resolved; 150 llvm::StringRef RemainderDir = path::parent_path(Remainder); 151 if (!RemainderDir.empty()) { 152 // Append the remaining path to the resolved directory. 153 Storage.append(path::get_separator()); 154 Storage.append(RemainderDir); 155 } 156 SearchDir = Storage; 157 } else { 158 SearchDir = path::parent_path(CompletionBuffer); 159 } 160 161 size_t FullPrefixLen = CompletionBuffer.size(); 162 163 PartialItem = path::filename(CompletionBuffer); 164 165 // path::filename() will return "." when the passed path ends with a 166 // directory separator. We have to filter those out, but only when the 167 // "." doesn't come from the completion request itself. 168 if (PartialItem == "." && path::is_separator(CompletionBuffer.back())) 169 PartialItem = llvm::StringRef(); 170 171 if (SearchDir.empty()) { 172 llvm::sys::fs::current_path(Storage); 173 SearchDir = Storage; 174 } 175 assert(!PartialItem.contains(path::get_separator())); 176 177 // SearchDir now contains the directory to search in, and Prefix contains the 178 // text we want to match against items in that directory. 179 180 FileSystem &fs = FileSystem::Instance(); 181 std::error_code EC; 182 llvm::vfs::directory_iterator Iter = fs.DirBegin(SearchDir, EC); 183 llvm::vfs::directory_iterator End; 184 for (; Iter != End && !EC; Iter.increment(EC)) { 185 auto &Entry = *Iter; 186 llvm::ErrorOr<llvm::vfs::Status> Status = fs.GetStatus(Entry.path()); 187 188 if (!Status) 189 continue; 190 191 auto Name = path::filename(Entry.path()); 192 193 // Omit ".", ".." 194 if (Name == "." || Name == ".." || !Name.startswith(PartialItem)) 195 continue; 196 197 bool is_dir = Status->isDirectory(); 198 199 // If it's a symlink, then we treat it as a directory as long as the target 200 // is a directory. 201 if (Status->isSymlink()) { 202 FileSpec symlink_filespec(Entry.path()); 203 FileSpec resolved_filespec; 204 auto error = fs.ResolveSymbolicLink(symlink_filespec, resolved_filespec); 205 if (error.Success()) 206 is_dir = fs.IsDirectory(symlink_filespec); 207 } 208 209 if (only_directories && !is_dir) 210 continue; 211 212 // Shrink it back down so that it just has the original prefix the user 213 // typed and remove the part of the name which is common to the located 214 // item and what the user typed. 215 CompletionBuffer.resize(FullPrefixLen); 216 Name = Name.drop_front(PartialItem.size()); 217 CompletionBuffer.append(Name); 218 219 if (is_dir) { 220 path::append(CompletionBuffer, path::get_separator()); 221 } 222 223 matches.AppendString(CompletionBuffer); 224 } 225 226 return matches.GetSize(); 227 } 228 229 static int DiskFilesOrDirectories(CompletionRequest &request, 230 bool only_directories) { 231 request.SetWordComplete(false); 232 StandardTildeExpressionResolver resolver; 233 StringList matches; 234 DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories, 235 matches, resolver); 236 request.AddCompletions(matches); 237 return request.GetNumberOfMatches(); 238 } 239 240 int CommandCompletions::DiskFiles(CommandInterpreter &interpreter, 241 CompletionRequest &request, 242 SearchFilter *searcher) { 243 return DiskFilesOrDirectories(request, /*only_dirs*/ false); 244 } 245 246 int CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name, 247 StringList &matches, 248 TildeExpressionResolver &Resolver) { 249 return DiskFilesOrDirectories(partial_file_name, false, matches, Resolver); 250 } 251 252 int CommandCompletions::DiskDirectories(CommandInterpreter &interpreter, 253 CompletionRequest &request, 254 SearchFilter *searcher) { 255 return DiskFilesOrDirectories(request, /*only_dirs*/ true); 256 } 257 258 int CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name, 259 StringList &matches, 260 TildeExpressionResolver &Resolver) { 261 return DiskFilesOrDirectories(partial_file_name, true, matches, Resolver); 262 } 263 264 int CommandCompletions::Modules(CommandInterpreter &interpreter, 265 CompletionRequest &request, 266 SearchFilter *searcher) { 267 request.SetWordComplete(true); 268 ModuleCompleter completer(interpreter, request); 269 270 if (searcher == nullptr) { 271 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); 272 SearchFilterForUnconstrainedSearches null_searcher(target_sp); 273 completer.DoCompletion(&null_searcher); 274 } else { 275 completer.DoCompletion(searcher); 276 } 277 return request.GetNumberOfMatches(); 278 } 279 280 int CommandCompletions::Symbols(CommandInterpreter &interpreter, 281 CompletionRequest &request, 282 SearchFilter *searcher) { 283 request.SetWordComplete(true); 284 SymbolCompleter completer(interpreter, request); 285 286 if (searcher == nullptr) { 287 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); 288 SearchFilterForUnconstrainedSearches null_searcher(target_sp); 289 completer.DoCompletion(&null_searcher); 290 } else { 291 completer.DoCompletion(searcher); 292 } 293 return request.GetNumberOfMatches(); 294 } 295 296 int CommandCompletions::SettingsNames(CommandInterpreter &interpreter, 297 CompletionRequest &request, 298 SearchFilter *searcher) { 299 // Cache the full setting name list 300 static StringList g_property_names; 301 if (g_property_names.GetSize() == 0) { 302 // Generate the full setting name list on demand 303 lldb::OptionValuePropertiesSP properties_sp( 304 interpreter.GetDebugger().GetValueProperties()); 305 if (properties_sp) { 306 StreamString strm; 307 properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName); 308 const std::string &str = strm.GetString(); 309 g_property_names.SplitIntoLines(str.c_str(), str.size()); 310 } 311 } 312 313 size_t exact_matches_idx = SIZE_MAX; 314 StringList matches; 315 g_property_names.AutoComplete(request.GetCursorArgumentPrefix(), matches, 316 exact_matches_idx); 317 request.SetWordComplete(exact_matches_idx != SIZE_MAX); 318 request.AddCompletions(matches); 319 return request.GetNumberOfMatches(); 320 } 321 322 int CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter, 323 CompletionRequest &request, 324 SearchFilter *searcher) { 325 StringList new_matches; 326 std::size_t num_matches = PluginManager::AutoCompletePlatformName( 327 request.GetCursorArgumentPrefix(), new_matches); 328 request.SetWordComplete(num_matches == 1); 329 request.AddCompletions(new_matches); 330 return request.GetNumberOfMatches(); 331 } 332 333 int CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter, 334 CompletionRequest &request, 335 SearchFilter *searcher) { 336 const uint32_t num_matches = ArchSpec::AutoComplete(request); 337 request.SetWordComplete(num_matches == 1); 338 return num_matches; 339 } 340 341 int CommandCompletions::VariablePath(CommandInterpreter &interpreter, 342 CompletionRequest &request, 343 SearchFilter *searcher) { 344 return Variable::AutoComplete(interpreter.GetExecutionContext(), request); 345 } 346 347 CommandCompletions::Completer::Completer(CommandInterpreter &interpreter, 348 CompletionRequest &request) 349 : m_interpreter(interpreter), m_request(request) {} 350 351 CommandCompletions::Completer::~Completer() = default; 352 353 //---------------------------------------------------------------------- 354 // SourceFileCompleter 355 //---------------------------------------------------------------------- 356 357 CommandCompletions::SourceFileCompleter::SourceFileCompleter( 358 CommandInterpreter &interpreter, bool include_support_files, 359 CompletionRequest &request) 360 : CommandCompletions::Completer(interpreter, request), 361 m_include_support_files(include_support_files), m_matching_files() { 362 FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); 363 m_file_name = partial_spec.GetFilename().GetCString(); 364 m_dir_name = partial_spec.GetDirectory().GetCString(); 365 } 366 367 lldb::SearchDepth CommandCompletions::SourceFileCompleter::GetDepth() { 368 return lldb::eSearchDepthCompUnit; 369 } 370 371 Searcher::CallbackReturn 372 CommandCompletions::SourceFileCompleter::SearchCallback(SearchFilter &filter, 373 SymbolContext &context, 374 Address *addr, 375 bool complete) { 376 if (context.comp_unit != nullptr) { 377 if (m_include_support_files) { 378 FileSpecList supporting_files = context.comp_unit->GetSupportFiles(); 379 for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) { 380 const FileSpec &sfile_spec = 381 supporting_files.GetFileSpecAtIndex(sfiles); 382 const char *sfile_file_name = sfile_spec.GetFilename().GetCString(); 383 const char *sfile_dir_name = sfile_spec.GetFilename().GetCString(); 384 bool match = false; 385 if (m_file_name && sfile_file_name && 386 strstr(sfile_file_name, m_file_name) == sfile_file_name) 387 match = true; 388 if (match && m_dir_name && sfile_dir_name && 389 strstr(sfile_dir_name, m_dir_name) != sfile_dir_name) 390 match = false; 391 392 if (match) { 393 m_matching_files.AppendIfUnique(sfile_spec); 394 } 395 } 396 } else { 397 const char *cur_file_name = context.comp_unit->GetFilename().GetCString(); 398 const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString(); 399 400 bool match = false; 401 if (m_file_name && cur_file_name && 402 strstr(cur_file_name, m_file_name) == cur_file_name) 403 match = true; 404 405 if (match && m_dir_name && cur_dir_name && 406 strstr(cur_dir_name, m_dir_name) != cur_dir_name) 407 match = false; 408 409 if (match) { 410 m_matching_files.AppendIfUnique(context.comp_unit); 411 } 412 } 413 } 414 return Searcher::eCallbackReturnContinue; 415 } 416 417 size_t 418 CommandCompletions::SourceFileCompleter::DoCompletion(SearchFilter *filter) { 419 filter->Search(*this); 420 // Now convert the filelist to completions: 421 for (size_t i = 0; i < m_matching_files.GetSize(); i++) { 422 m_request.AddCompletion( 423 m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); 424 } 425 return m_request.GetNumberOfMatches(); 426 } 427 428 //---------------------------------------------------------------------- 429 // SymbolCompleter 430 //---------------------------------------------------------------------- 431 432 static bool regex_chars(const char comp) { 433 return (comp == '[' || comp == ']' || comp == '(' || comp == ')' || 434 comp == '{' || comp == '}' || comp == '+' || comp == '.' || 435 comp == '*' || comp == '|' || comp == '^' || comp == '$' || 436 comp == '\\' || comp == '?'); 437 } 438 439 CommandCompletions::SymbolCompleter::SymbolCompleter( 440 CommandInterpreter &interpreter, CompletionRequest &request) 441 : CommandCompletions::Completer(interpreter, request) { 442 std::string regex_str; 443 if (!m_request.GetCursorArgumentPrefix().empty()) { 444 regex_str.append("^"); 445 regex_str.append(m_request.GetCursorArgumentPrefix()); 446 } else { 447 // Match anything since the completion string is empty 448 regex_str.append("."); 449 } 450 std::string::iterator pos = 451 find_if(regex_str.begin() + 1, regex_str.end(), regex_chars); 452 while (pos < regex_str.end()) { 453 pos = regex_str.insert(pos, '\\'); 454 pos = find_if(pos + 2, regex_str.end(), regex_chars); 455 } 456 m_regex.Compile(regex_str); 457 } 458 459 lldb::SearchDepth CommandCompletions::SymbolCompleter::GetDepth() { 460 return lldb::eSearchDepthModule; 461 } 462 463 Searcher::CallbackReturn CommandCompletions::SymbolCompleter::SearchCallback( 464 SearchFilter &filter, SymbolContext &context, Address *addr, 465 bool complete) { 466 if (context.module_sp) { 467 SymbolContextList sc_list; 468 const bool include_symbols = true; 469 const bool include_inlines = true; 470 const bool append = true; 471 context.module_sp->FindFunctions(m_regex, include_symbols, include_inlines, 472 append, sc_list); 473 474 SymbolContext sc; 475 // Now add the functions & symbols to the list - only add if unique: 476 for (uint32_t i = 0; i < sc_list.GetSize(); i++) { 477 if (sc_list.GetContextAtIndex(i, sc)) { 478 ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled); 479 if (!func_name.IsEmpty()) 480 m_match_set.insert(func_name); 481 } 482 } 483 } 484 return Searcher::eCallbackReturnContinue; 485 } 486 487 size_t CommandCompletions::SymbolCompleter::DoCompletion(SearchFilter *filter) { 488 filter->Search(*this); 489 collection::iterator pos = m_match_set.begin(), end = m_match_set.end(); 490 for (pos = m_match_set.begin(); pos != end; pos++) 491 m_request.AddCompletion((*pos).GetCString()); 492 493 return m_request.GetNumberOfMatches(); 494 } 495 496 //---------------------------------------------------------------------- 497 // ModuleCompleter 498 //---------------------------------------------------------------------- 499 CommandCompletions::ModuleCompleter::ModuleCompleter( 500 CommandInterpreter &interpreter, CompletionRequest &request) 501 : CommandCompletions::Completer(interpreter, request) { 502 FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); 503 m_file_name = partial_spec.GetFilename().GetCString(); 504 m_dir_name = partial_spec.GetDirectory().GetCString(); 505 } 506 507 lldb::SearchDepth CommandCompletions::ModuleCompleter::GetDepth() { 508 return lldb::eSearchDepthModule; 509 } 510 511 Searcher::CallbackReturn CommandCompletions::ModuleCompleter::SearchCallback( 512 SearchFilter &filter, SymbolContext &context, Address *addr, 513 bool complete) { 514 if (context.module_sp) { 515 const char *cur_file_name = 516 context.module_sp->GetFileSpec().GetFilename().GetCString(); 517 const char *cur_dir_name = 518 context.module_sp->GetFileSpec().GetDirectory().GetCString(); 519 520 bool match = false; 521 if (m_file_name && cur_file_name && 522 strstr(cur_file_name, m_file_name) == cur_file_name) 523 match = true; 524 525 if (match && m_dir_name && cur_dir_name && 526 strstr(cur_dir_name, m_dir_name) != cur_dir_name) 527 match = false; 528 529 if (match) { 530 m_request.AddCompletion(cur_file_name); 531 } 532 } 533 return Searcher::eCallbackReturnContinue; 534 } 535 536 size_t CommandCompletions::ModuleCompleter::DoCompletion(SearchFilter *filter) { 537 filter->Search(*this); 538 return m_request.GetNumberOfMatches(); 539 } 540