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 // C Includes 11 #include <sys/stat.h> 12 #if defined(__APPLE__) || defined(__linux__) 13 #include <pwd.h> 14 #endif 15 16 // C++ Includes 17 // Other libraries and framework includes 18 #include "llvm/ADT/SmallString.h" 19 20 // Project includes 21 #include "lldb/Core/FileSpecList.h" 22 #include "lldb/Core/Module.h" 23 #include "lldb/Core/PluginManager.h" 24 #include "lldb/Host/FileSpec.h" 25 #include "lldb/Host/FileSystem.h" 26 #include "lldb/Interpreter/Args.h" 27 #include "lldb/Interpreter/CommandCompletions.h" 28 #include "lldb/Interpreter/CommandInterpreter.h" 29 #include "lldb/Interpreter/OptionValueProperties.h" 30 #include "lldb/Symbol/CompileUnit.h" 31 #include "lldb/Symbol/Variable.h" 32 #include "lldb/Target/Target.h" 33 #include "lldb/Utility/CleanUp.h" 34 35 #include "llvm/ADT/SmallString.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 llvm::StringRef completion_str, int match_start_point, 57 int max_return_elements, SearchFilter *searcher, bool &word_complete, 58 StringList &matches) { 59 bool handled = false; 60 61 if (completion_mask & eCustomCompletion) 62 return false; 63 64 for (int i = 0;; i++) { 65 if (g_common_completions[i].type == eNoCompletion) 66 break; 67 else if ((g_common_completions[i].type & completion_mask) == 68 g_common_completions[i].type && 69 g_common_completions[i].callback != nullptr) { 70 handled = true; 71 g_common_completions[i].callback(interpreter, completion_str, 72 match_start_point, max_return_elements, 73 searcher, word_complete, matches); 74 } 75 } 76 return handled; 77 } 78 79 int CommandCompletions::SourceFiles(CommandInterpreter &interpreter, 80 llvm::StringRef partial_file_name, 81 int match_start_point, 82 int max_return_elements, 83 SearchFilter *searcher, bool &word_complete, 84 StringList &matches) { 85 word_complete = true; 86 // Find some way to switch "include support files..." 87 SourceFileCompleter completer(interpreter, false, partial_file_name, 88 match_start_point, max_return_elements, 89 matches); 90 91 if (searcher == nullptr) { 92 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); 93 SearchFilterForUnconstrainedSearches null_searcher(target_sp); 94 completer.DoCompletion(&null_searcher); 95 } else { 96 completer.DoCompletion(searcher); 97 } 98 return matches.GetSize(); 99 } 100 101 typedef struct DiskFilesOrDirectoriesBaton { 102 const char *remainder; 103 char *partial_name_copy; 104 bool only_directories; 105 bool *saw_directory; 106 StringList *matches; 107 char *end_ptr; 108 size_t baselen; 109 } DiskFilesOrDirectoriesBaton; 110 111 FileSpec::EnumerateDirectoryResult 112 DiskFilesOrDirectoriesCallback(void *baton, FileSpec::FileType file_type, 113 const FileSpec &spec) { 114 const char *name = spec.GetFilename().AsCString(); 115 116 const DiskFilesOrDirectoriesBaton *parameters = 117 (DiskFilesOrDirectoriesBaton *)baton; 118 char *end_ptr = parameters->end_ptr; 119 char *partial_name_copy = parameters->partial_name_copy; 120 const char *remainder = parameters->remainder; 121 122 // Omit ".", ".." and any . files if the match string doesn't start with . 123 if (name[0] == '.') { 124 if (name[1] == '\0') 125 return FileSpec::eEnumerateDirectoryResultNext; 126 else if (name[1] == '.' && name[2] == '\0') 127 return FileSpec::eEnumerateDirectoryResultNext; 128 else if (remainder[0] != '.') 129 return FileSpec::eEnumerateDirectoryResultNext; 130 } 131 132 // If we found a directory, we put a "/" at the end of the name. 133 134 if (remainder[0] == '\0' || strstr(name, remainder) == name) { 135 if (strlen(name) + parameters->baselen >= PATH_MAX) 136 return FileSpec::eEnumerateDirectoryResultNext; 137 138 strcpy(end_ptr, name); 139 140 bool isa_directory = false; 141 if (file_type == FileSpec::eFileTypeDirectory) 142 isa_directory = true; 143 else if (file_type == FileSpec::eFileTypeSymbolicLink) { 144 if (FileSpec(partial_name_copy, false).IsDirectory()) 145 isa_directory = true; 146 } 147 148 if (isa_directory) { 149 *parameters->saw_directory = true; 150 size_t len = strlen(parameters->partial_name_copy); 151 partial_name_copy[len] = '/'; 152 partial_name_copy[len + 1] = '\0'; 153 } 154 if (parameters->only_directories && !isa_directory) 155 return FileSpec::eEnumerateDirectoryResultNext; 156 parameters->matches->AppendString(partial_name_copy); 157 } 158 159 return FileSpec::eEnumerateDirectoryResultNext; 160 } 161 162 static int DiskFilesOrDirectories(llvm::StringRef partial_file_name, 163 bool only_directories, bool &saw_directory, 164 StringList &matches) { 165 // I'm going to use the "glob" function with GLOB_TILDE for user directory 166 // expansion. 167 // If it is not defined on your host system, you'll need to implement it 168 // yourself... 169 170 size_t partial_name_len = partial_file_name.size(); 171 172 if (partial_name_len >= PATH_MAX) 173 return matches.GetSize(); 174 175 // This copy of the string will be cut up into the directory part, and the 176 // remainder. end_ptr below will point to the place of the remainder in this 177 // string. Then when we've resolved the containing directory, and opened it, 178 // we'll read the directory contents and overwrite the partial_name_copy 179 // starting from end_ptr with each of the matches. Thus we will preserve the 180 // form the user originally typed. 181 182 char partial_name_copy[PATH_MAX]; 183 memcpy(partial_name_copy, partial_file_name.data(), partial_name_len); 184 partial_name_copy[partial_name_len] = '\0'; 185 186 // We'll need to save a copy of the remainder for comparison, which we do 187 // here. 188 char remainder[PATH_MAX]; 189 190 // end_ptr will point past the last / in partial_name_copy, or if there is no 191 // slash to the beginning of the string. 192 char *end_ptr; 193 194 end_ptr = strrchr(partial_name_copy, '/'); 195 196 // This will store the resolved form of the containing directory 197 llvm::SmallString<64> containing_part; 198 199 if (end_ptr == nullptr) { 200 // There's no directory. If the thing begins with a "~" then this is a bare 201 // user name. 202 if (*partial_name_copy == '~') { 203 // Nothing here but the user name. We could just put a slash on the end, 204 // but for completeness sake we'll resolve the user name and only put a 205 // slash 206 // on the end if it exists. 207 llvm::SmallString<64> resolved_username(partial_name_copy); 208 FileSpec::ResolveUsername(resolved_username); 209 210 // Not sure how this would happen, a username longer than PATH_MAX? 211 // Still... 212 if (resolved_username.size() == 0) { 213 // The user name didn't resolve, let's look in the password database for 214 // matches. 215 // The user name database contains duplicates, and is not in 216 // alphabetical order, so 217 // we'll use a set to manage that for us. 218 FileSpec::ResolvePartialUsername(partial_name_copy, matches); 219 if (matches.GetSize() > 0) 220 saw_directory = true; 221 return matches.GetSize(); 222 } else { 223 // The thing exists, put a '/' on the end, and return it... 224 // FIXME: complete user names here: 225 partial_name_copy[partial_name_len] = '/'; 226 partial_name_copy[partial_name_len + 1] = '\0'; 227 matches.AppendString(partial_name_copy); 228 saw_directory = true; 229 return matches.GetSize(); 230 } 231 } else { 232 // The containing part is the CWD, and the whole string is the remainder. 233 containing_part = "."; 234 strcpy(remainder, partial_name_copy); 235 end_ptr = partial_name_copy; 236 } 237 } else { 238 if (end_ptr == partial_name_copy) { 239 // We're completing a file or directory in the root volume. 240 containing_part = "/"; 241 } else { 242 containing_part.append(partial_name_copy, end_ptr); 243 } 244 // Push end_ptr past the final "/" and set remainder. 245 end_ptr++; 246 strcpy(remainder, end_ptr); 247 } 248 249 // Look for a user name in the containing part, and if it's there, resolve it 250 // and stick the 251 // result back into the containing_part: 252 253 if (*partial_name_copy == '~') { 254 FileSpec::ResolveUsername(containing_part); 255 // User name doesn't exist, we're not getting any further... 256 if (containing_part.empty()) 257 return matches.GetSize(); 258 } 259 260 // Okay, containing_part is now the directory we want to open and look for 261 // files: 262 263 size_t baselen = end_ptr - partial_name_copy; 264 265 DiskFilesOrDirectoriesBaton parameters; 266 parameters.remainder = remainder; 267 parameters.partial_name_copy = partial_name_copy; 268 parameters.only_directories = only_directories; 269 parameters.saw_directory = &saw_directory; 270 parameters.matches = &matches; 271 parameters.end_ptr = end_ptr; 272 parameters.baselen = baselen; 273 274 FileSpec::EnumerateDirectory(containing_part.c_str(), true, true, true, 275 DiskFilesOrDirectoriesCallback, ¶meters); 276 277 return matches.GetSize(); 278 } 279 280 int CommandCompletions::DiskFiles(CommandInterpreter &interpreter, 281 llvm::StringRef partial_file_name, 282 int match_start_point, 283 int max_return_elements, 284 SearchFilter *searcher, bool &word_complete, 285 StringList &matches) { 286 int ret_val = 287 DiskFilesOrDirectories(partial_file_name, false, word_complete, matches); 288 word_complete = !word_complete; 289 return ret_val; 290 } 291 292 int CommandCompletions::DiskDirectories( 293 CommandInterpreter &interpreter, llvm::StringRef partial_file_name, 294 int match_start_point, int max_return_elements, SearchFilter *searcher, 295 bool &word_complete, StringList &matches) { 296 int ret_val = 297 DiskFilesOrDirectories(partial_file_name, true, word_complete, matches); 298 word_complete = false; 299 return ret_val; 300 } 301 302 int CommandCompletions::Modules(CommandInterpreter &interpreter, 303 llvm::StringRef partial_file_name, 304 int match_start_point, int max_return_elements, 305 SearchFilter *searcher, bool &word_complete, 306 StringList &matches) { 307 word_complete = true; 308 ModuleCompleter completer(interpreter, partial_file_name, match_start_point, 309 max_return_elements, matches); 310 311 if (searcher == nullptr) { 312 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); 313 SearchFilterForUnconstrainedSearches null_searcher(target_sp); 314 completer.DoCompletion(&null_searcher); 315 } else { 316 completer.DoCompletion(searcher); 317 } 318 return matches.GetSize(); 319 } 320 321 int CommandCompletions::Symbols(CommandInterpreter &interpreter, 322 llvm::StringRef partial_file_name, 323 int match_start_point, int max_return_elements, 324 SearchFilter *searcher, bool &word_complete, 325 StringList &matches) { 326 word_complete = true; 327 SymbolCompleter completer(interpreter, partial_file_name, match_start_point, 328 max_return_elements, matches); 329 330 if (searcher == nullptr) { 331 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); 332 SearchFilterForUnconstrainedSearches null_searcher(target_sp); 333 completer.DoCompletion(&null_searcher); 334 } else { 335 completer.DoCompletion(searcher); 336 } 337 return matches.GetSize(); 338 } 339 340 int CommandCompletions::SettingsNames( 341 CommandInterpreter &interpreter, llvm::StringRef partial_setting_name, 342 int match_start_point, int max_return_elements, SearchFilter *searcher, 343 bool &word_complete, StringList &matches) { 344 // Cache the full setting name list 345 static StringList g_property_names; 346 if (g_property_names.GetSize() == 0) { 347 // Generate the full setting name list on demand 348 lldb::OptionValuePropertiesSP properties_sp( 349 interpreter.GetDebugger().GetValueProperties()); 350 if (properties_sp) { 351 StreamString strm; 352 properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName); 353 const std::string &str = strm.GetString(); 354 g_property_names.SplitIntoLines(str.c_str(), str.size()); 355 } 356 } 357 358 size_t exact_matches_idx = SIZE_MAX; 359 const size_t num_matches = g_property_names.AutoComplete( 360 partial_setting_name, matches, exact_matches_idx); 361 word_complete = exact_matches_idx != SIZE_MAX; 362 return num_matches; 363 } 364 365 int CommandCompletions::PlatformPluginNames( 366 CommandInterpreter &interpreter, llvm::StringRef partial_name, 367 int match_start_point, int max_return_elements, SearchFilter *searcher, 368 bool &word_complete, lldb_private::StringList &matches) { 369 const uint32_t num_matches = 370 PluginManager::AutoCompletePlatformName(partial_name, matches); 371 word_complete = num_matches == 1; 372 return num_matches; 373 } 374 375 int CommandCompletions::ArchitectureNames( 376 CommandInterpreter &interpreter, llvm::StringRef partial_name, 377 int match_start_point, int max_return_elements, SearchFilter *searcher, 378 bool &word_complete, lldb_private::StringList &matches) { 379 const uint32_t num_matches = ArchSpec::AutoComplete(partial_name, matches); 380 word_complete = num_matches == 1; 381 return num_matches; 382 } 383 384 int CommandCompletions::VariablePath( 385 CommandInterpreter &interpreter, llvm::StringRef partial_name, 386 int match_start_point, int max_return_elements, SearchFilter *searcher, 387 bool &word_complete, lldb_private::StringList &matches) { 388 return Variable::AutoComplete(interpreter.GetExecutionContext(), partial_name, 389 matches, word_complete); 390 } 391 392 CommandCompletions::Completer::Completer(CommandInterpreter &interpreter, 393 llvm::StringRef completion_str, 394 int match_start_point, 395 int max_return_elements, 396 StringList &matches) 397 : m_interpreter(interpreter), m_completion_str(completion_str), 398 m_match_start_point(match_start_point), 399 m_max_return_elements(max_return_elements), m_matches(matches) {} 400 401 CommandCompletions::Completer::~Completer() = default; 402 403 //---------------------------------------------------------------------- 404 // SourceFileCompleter 405 //---------------------------------------------------------------------- 406 407 CommandCompletions::SourceFileCompleter::SourceFileCompleter( 408 CommandInterpreter &interpreter, bool include_support_files, 409 llvm::StringRef completion_str, int match_start_point, 410 int max_return_elements, StringList &matches) 411 : CommandCompletions::Completer(interpreter, completion_str, 412 match_start_point, max_return_elements, 413 matches), 414 m_include_support_files(include_support_files), m_matching_files() { 415 FileSpec partial_spec(m_completion_str, false); 416 m_file_name = partial_spec.GetFilename().GetCString(); 417 m_dir_name = partial_spec.GetDirectory().GetCString(); 418 } 419 420 Searcher::Depth CommandCompletions::SourceFileCompleter::GetDepth() { 421 return eDepthCompUnit; 422 } 423 424 Searcher::CallbackReturn 425 CommandCompletions::SourceFileCompleter::SearchCallback(SearchFilter &filter, 426 SymbolContext &context, 427 Address *addr, 428 bool complete) { 429 if (context.comp_unit != nullptr) { 430 if (m_include_support_files) { 431 FileSpecList supporting_files = context.comp_unit->GetSupportFiles(); 432 for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) { 433 const FileSpec &sfile_spec = 434 supporting_files.GetFileSpecAtIndex(sfiles); 435 const char *sfile_file_name = sfile_spec.GetFilename().GetCString(); 436 const char *sfile_dir_name = sfile_spec.GetFilename().GetCString(); 437 bool match = false; 438 if (m_file_name && sfile_file_name && 439 strstr(sfile_file_name, m_file_name) == sfile_file_name) 440 match = true; 441 if (match && m_dir_name && sfile_dir_name && 442 strstr(sfile_dir_name, m_dir_name) != sfile_dir_name) 443 match = false; 444 445 if (match) { 446 m_matching_files.AppendIfUnique(sfile_spec); 447 } 448 } 449 } else { 450 const char *cur_file_name = context.comp_unit->GetFilename().GetCString(); 451 const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString(); 452 453 bool match = false; 454 if (m_file_name && cur_file_name && 455 strstr(cur_file_name, m_file_name) == cur_file_name) 456 match = true; 457 458 if (match && m_dir_name && cur_dir_name && 459 strstr(cur_dir_name, m_dir_name) != cur_dir_name) 460 match = false; 461 462 if (match) { 463 m_matching_files.AppendIfUnique(context.comp_unit); 464 } 465 } 466 } 467 return Searcher::eCallbackReturnContinue; 468 } 469 470 size_t 471 CommandCompletions::SourceFileCompleter::DoCompletion(SearchFilter *filter) { 472 filter->Search(*this); 473 // Now convert the filelist to completions: 474 for (size_t i = 0; i < m_matching_files.GetSize(); i++) { 475 m_matches.AppendString( 476 m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); 477 } 478 return m_matches.GetSize(); 479 } 480 481 //---------------------------------------------------------------------- 482 // SymbolCompleter 483 //---------------------------------------------------------------------- 484 485 static bool regex_chars(const char comp) { 486 return (comp == '[' || comp == ']' || comp == '(' || comp == ')' || 487 comp == '{' || comp == '}' || comp == '+' || comp == '.' || 488 comp == '*' || comp == '|' || comp == '^' || comp == '$' || 489 comp == '\\' || comp == '?'); 490 } 491 492 CommandCompletions::SymbolCompleter::SymbolCompleter( 493 CommandInterpreter &interpreter, llvm::StringRef completion_str, 494 int match_start_point, int max_return_elements, StringList &matches) 495 : CommandCompletions::Completer(interpreter, completion_str, 496 match_start_point, max_return_elements, 497 matches) { 498 std::string regex_str; 499 if (!completion_str.empty()) { 500 regex_str.append("^"); 501 regex_str.append(completion_str); 502 } else { 503 // Match anything since the completion string is empty 504 regex_str.append("."); 505 } 506 std::string::iterator pos = 507 find_if(regex_str.begin() + 1, regex_str.end(), regex_chars); 508 while (pos < regex_str.end()) { 509 pos = regex_str.insert(pos, '\\'); 510 pos = find_if(pos + 2, regex_str.end(), regex_chars); 511 } 512 m_regex.Compile(regex_str); 513 } 514 515 Searcher::Depth CommandCompletions::SymbolCompleter::GetDepth() { 516 return eDepthModule; 517 } 518 519 Searcher::CallbackReturn CommandCompletions::SymbolCompleter::SearchCallback( 520 SearchFilter &filter, SymbolContext &context, Address *addr, 521 bool complete) { 522 if (context.module_sp) { 523 SymbolContextList sc_list; 524 const bool include_symbols = true; 525 const bool include_inlines = true; 526 const bool append = true; 527 context.module_sp->FindFunctions(m_regex, include_symbols, include_inlines, 528 append, sc_list); 529 530 SymbolContext sc; 531 // Now add the functions & symbols to the list - only add if unique: 532 for (uint32_t i = 0; i < sc_list.GetSize(); i++) { 533 if (sc_list.GetContextAtIndex(i, sc)) { 534 ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled); 535 if (!func_name.IsEmpty()) 536 m_match_set.insert(func_name); 537 } 538 } 539 } 540 return Searcher::eCallbackReturnContinue; 541 } 542 543 size_t CommandCompletions::SymbolCompleter::DoCompletion(SearchFilter *filter) { 544 filter->Search(*this); 545 collection::iterator pos = m_match_set.begin(), end = m_match_set.end(); 546 for (pos = m_match_set.begin(); pos != end; pos++) 547 m_matches.AppendString((*pos).GetCString()); 548 549 return m_matches.GetSize(); 550 } 551 552 //---------------------------------------------------------------------- 553 // ModuleCompleter 554 //---------------------------------------------------------------------- 555 CommandCompletions::ModuleCompleter::ModuleCompleter( 556 CommandInterpreter &interpreter, llvm::StringRef completion_str, 557 int match_start_point, int max_return_elements, StringList &matches) 558 : CommandCompletions::Completer(interpreter, completion_str, 559 match_start_point, max_return_elements, 560 matches) { 561 FileSpec partial_spec(m_completion_str, false); 562 m_file_name = partial_spec.GetFilename().GetCString(); 563 m_dir_name = partial_spec.GetDirectory().GetCString(); 564 } 565 566 Searcher::Depth CommandCompletions::ModuleCompleter::GetDepth() { 567 return eDepthModule; 568 } 569 570 Searcher::CallbackReturn CommandCompletions::ModuleCompleter::SearchCallback( 571 SearchFilter &filter, SymbolContext &context, Address *addr, 572 bool complete) { 573 if (context.module_sp) { 574 const char *cur_file_name = 575 context.module_sp->GetFileSpec().GetFilename().GetCString(); 576 const char *cur_dir_name = 577 context.module_sp->GetFileSpec().GetDirectory().GetCString(); 578 579 bool match = false; 580 if (m_file_name && cur_file_name && 581 strstr(cur_file_name, m_file_name) == cur_file_name) 582 match = true; 583 584 if (match && m_dir_name && cur_dir_name && 585 strstr(cur_dir_name, m_dir_name) != cur_dir_name) 586 match = false; 587 588 if (match) { 589 m_matches.AppendString(cur_file_name); 590 } 591 } 592 return Searcher::eCallbackReturnContinue; 593 } 594 595 size_t CommandCompletions::ModuleCompleter::DoCompletion(SearchFilter *filter) { 596 filter->Search(*this); 597 return m_matches.GetSize(); 598 } 599