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, &parameters);
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