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     lldb::UserSettingsControllerSP root_settings = Debugger::GetSettingsController();
409     Args partial_setting_name_pieces = UserSettingsController::BreakNameIntoPieces (partial_setting_name);
410 
411     return UserSettingsController::CompleteSettingsNames (root_settings,
412                                                           partial_setting_name_pieces,
413                                                           word_complete,
414                                                           matches);
415 
416     //return matches.GetSize();
417 }
418 
419 
420 int
421 CommandCompletions::PlatformPluginNames (CommandInterpreter &interpreter,
422                                          const char *partial_name,
423                                          int match_start_point,
424                                          int max_return_elements,
425                                          SearchFilter *searcher,
426                                          bool &word_complete,
427                                          lldb_private::StringList &matches)
428 {
429     const uint32_t num_matches = PluginManager::AutoCompletePlatformName(partial_name, matches);
430     word_complete = num_matches == 1;
431     return num_matches;
432 }
433 
434 int
435 CommandCompletions::ArchitectureNames (CommandInterpreter &interpreter,
436                                        const char *partial_name,
437                                        int match_start_point,
438                                        int max_return_elements,
439                                        SearchFilter *searcher,
440                                        bool &word_complete,
441                                        lldb_private::StringList &matches)
442 {
443     const uint32_t num_matches = ArchSpec::AutoComplete (partial_name, matches);
444     word_complete = num_matches == 1;
445     return num_matches;
446 }
447 
448 
449 CommandCompletions::Completer::Completer
450 (
451     CommandInterpreter &interpreter,
452     const char *completion_str,
453     int match_start_point,
454     int max_return_elements,
455     StringList &matches
456 ) :
457     m_interpreter (interpreter),
458     m_completion_str (completion_str),
459     m_match_start_point (match_start_point),
460     m_max_return_elements (max_return_elements),
461     m_matches (matches)
462 {
463 }
464 
465 CommandCompletions::Completer::~Completer ()
466 {
467 
468 }
469 
470 //----------------------------------------------------------------------
471 // SourceFileCompleter
472 //----------------------------------------------------------------------
473 
474 CommandCompletions::SourceFileCompleter::SourceFileCompleter
475 (
476     CommandInterpreter &interpreter,
477     bool include_support_files,
478     const char *completion_str,
479     int match_start_point,
480     int max_return_elements,
481     StringList &matches
482 ) :
483     CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches),
484     m_include_support_files (include_support_files),
485     m_matching_files()
486 {
487     FileSpec partial_spec (m_completion_str.c_str(), false);
488     m_file_name = partial_spec.GetFilename().GetCString();
489     m_dir_name = partial_spec.GetDirectory().GetCString();
490 }
491 
492 Searcher::Depth
493 CommandCompletions::SourceFileCompleter::GetDepth()
494 {
495     return eDepthCompUnit;
496 }
497 
498 Searcher::CallbackReturn
499 CommandCompletions::SourceFileCompleter::SearchCallback (
500     SearchFilter &filter,
501     SymbolContext &context,
502     Address *addr,
503     bool complete
504 )
505 {
506     if (context.comp_unit != NULL)
507     {
508         if (m_include_support_files)
509         {
510             FileSpecList supporting_files = context.comp_unit->GetSupportFiles();
511             for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++)
512             {
513                 const FileSpec &sfile_spec = supporting_files.GetFileSpecAtIndex(sfiles);
514                 const char *sfile_file_name = sfile_spec.GetFilename().GetCString();
515                 const char *sfile_dir_name = sfile_spec.GetFilename().GetCString();
516                 bool match = false;
517                 if (m_file_name && sfile_file_name
518                     && strstr (sfile_file_name, m_file_name) == sfile_file_name)
519                     match = true;
520                 if (match && m_dir_name && sfile_dir_name
521                     && strstr (sfile_dir_name, m_dir_name) != sfile_dir_name)
522                     match = false;
523 
524                 if (match)
525                 {
526                     m_matching_files.AppendIfUnique(sfile_spec);
527                 }
528             }
529 
530         }
531         else
532         {
533             const char *cur_file_name = context.comp_unit->GetFilename().GetCString();
534             const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString();
535 
536             bool match = false;
537             if (m_file_name && cur_file_name
538                 && strstr (cur_file_name, m_file_name) == cur_file_name)
539                 match = true;
540 
541             if (match && m_dir_name && cur_dir_name
542                 && strstr (cur_dir_name, m_dir_name) != cur_dir_name)
543                 match = false;
544 
545             if (match)
546             {
547                 m_matching_files.AppendIfUnique(context.comp_unit);
548             }
549         }
550     }
551     return Searcher::eCallbackReturnContinue;
552 }
553 
554 size_t
555 CommandCompletions::SourceFileCompleter::DoCompletion (SearchFilter *filter)
556 {
557     filter->Search (*this);
558     // Now convert the filelist to completions:
559     for (size_t i = 0; i < m_matching_files.GetSize(); i++)
560     {
561         m_matches.AppendString (m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());
562     }
563     return m_matches.GetSize();
564 
565 }
566 
567 //----------------------------------------------------------------------
568 // SymbolCompleter
569 //----------------------------------------------------------------------
570 
571 static bool
572 regex_chars (const char comp)
573 {
574     if (comp == '[' || comp == ']' || comp == '(' || comp == ')')
575         return true;
576     else
577         return false;
578 }
579 CommandCompletions::SymbolCompleter::SymbolCompleter
580 (
581     CommandInterpreter &interpreter,
582     const char *completion_str,
583     int match_start_point,
584     int max_return_elements,
585     StringList &matches
586 ) :
587     CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches)
588 {
589     std::string regex_str ("^");
590     regex_str.append(completion_str);
591     regex_str.append(".*");
592     std::string::iterator pos;
593 
594     pos = find_if(regex_str.begin(), regex_str.end(), regex_chars);
595     while (pos < regex_str.end()) {
596         pos = regex_str.insert(pos, '\\');
597         pos += 2;
598         pos = find_if(pos, regex_str.end(), regex_chars);
599     }
600     m_regex.Compile(regex_str.c_str());
601 }
602 
603 Searcher::Depth
604 CommandCompletions::SymbolCompleter::GetDepth()
605 {
606     return eDepthModule;
607 }
608 
609 Searcher::CallbackReturn
610 CommandCompletions::SymbolCompleter::SearchCallback (
611     SearchFilter &filter,
612     SymbolContext &context,
613     Address *addr,
614     bool complete
615 )
616 {
617     if (context.module_sp)
618     {
619         SymbolContextList sc_list;
620         const bool include_symbols = true;
621         const bool append = true;
622         context.module_sp->FindFunctions (m_regex, include_symbols, append, sc_list);
623 
624         SymbolContext sc;
625         // Now add the functions & symbols to the list - only add if unique:
626         for (uint32_t i = 0; i < sc_list.GetSize(); i++)
627         {
628             if (sc_list.GetContextAtIndex(i, sc))
629             {
630                 ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled);
631                 if (!func_name.IsEmpty())
632                     m_match_set.insert (func_name);
633             }
634         }
635     }
636     return Searcher::eCallbackReturnContinue;
637 }
638 
639 size_t
640 CommandCompletions::SymbolCompleter::DoCompletion (SearchFilter *filter)
641 {
642     filter->Search (*this);
643     collection::iterator pos = m_match_set.begin(), end = m_match_set.end();
644     for (pos = m_match_set.begin(); pos != end; pos++)
645         m_matches.AppendString((*pos).GetCString());
646 
647     return m_matches.GetSize();
648 }
649 
650 //----------------------------------------------------------------------
651 // ModuleCompleter
652 //----------------------------------------------------------------------
653 CommandCompletions::ModuleCompleter::ModuleCompleter
654 (
655     CommandInterpreter &interpreter,
656     const char *completion_str,
657     int match_start_point,
658     int max_return_elements,
659     StringList &matches
660 ) :
661     CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches)
662 {
663     FileSpec partial_spec (m_completion_str.c_str(), false);
664     m_file_name = partial_spec.GetFilename().GetCString();
665     m_dir_name = partial_spec.GetDirectory().GetCString();
666 }
667 
668 Searcher::Depth
669 CommandCompletions::ModuleCompleter::GetDepth()
670 {
671     return eDepthModule;
672 }
673 
674 Searcher::CallbackReturn
675 CommandCompletions::ModuleCompleter::SearchCallback (
676     SearchFilter &filter,
677     SymbolContext &context,
678     Address *addr,
679     bool complete
680 )
681 {
682     if (context.module_sp)
683     {
684         const char *cur_file_name = context.module_sp->GetFileSpec().GetFilename().GetCString();
685         const char *cur_dir_name = context.module_sp->GetFileSpec().GetDirectory().GetCString();
686 
687         bool match = false;
688         if (m_file_name && cur_file_name
689             && strstr (cur_file_name, m_file_name) == cur_file_name)
690             match = true;
691 
692         if (match && m_dir_name && cur_dir_name
693             && strstr (cur_dir_name, m_dir_name) != cur_dir_name)
694             match = false;
695 
696         if (match)
697         {
698             m_matches.AppendString (cur_file_name);
699         }
700     }
701     return Searcher::eCallbackReturnContinue;
702 }
703 
704 size_t
705 CommandCompletions::ModuleCompleter::DoCompletion (SearchFilter *filter)
706 {
707     filter->Search (*this);
708     return m_matches.GetSize();
709 }
710