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