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