Home | History | Annotate | Download | only in Commands
      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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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 
    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 
    493 CommandCompletions::Completer::~Completer ()
    494 {
    495 
    496 }
    497 
    498 //----------------------------------------------------------------------
    499 // SourceFileCompleter
    500 //----------------------------------------------------------------------
    501 
    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
    521 CommandCompletions::SourceFileCompleter::GetDepth()
    522 {
    523     return eDepthCompUnit;
    524 }
    525 
    526 Searcher::CallbackReturn
    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
    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
    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 }
    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
    648 CommandCompletions::SymbolCompleter::GetDepth()
    649 {
    650     return eDepthModule;
    651 }
    652 
    653 Searcher::CallbackReturn
    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
    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 //----------------------------------------------------------------------
    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
    714 CommandCompletions::ModuleCompleter::GetDepth()
    715 {
    716     return eDepthModule;
    717 }
    718 
    719 Searcher::CallbackReturn
    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
    750 CommandCompletions::ModuleCompleter::DoCompletion (SearchFilter *filter)
    751 {
    752     filter->Search (*this);
    753     return m_matches.GetSize();
    754 }
    755