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