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