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