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