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