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
34using namespace lldb_private;
35
36CommandCompletions::CommonCompletionElement
37CommandCompletions::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
52bool
53CommandCompletions::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
90int
91CommandCompletions::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
124static int
125DiskFilesOrDirectories
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
300int
301CommandCompletions::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
321int
322CommandCompletions::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
341int
342CommandCompletions::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
373int
374CommandCompletions::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
404int
405CommandCompletions::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
435int
436CommandCompletions::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
449int
450CommandCompletions::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
464int
465CommandCompletions::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
477CommandCompletions::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
493CommandCompletions::Completer::~Completer ()
494{
495
496}
497
498//----------------------------------------------------------------------
499// SourceFileCompleter
500//----------------------------------------------------------------------
501
502CommandCompletions::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
520Searcher::Depth
521CommandCompletions::SourceFileCompleter::GetDepth()
522{
523    return eDepthCompUnit;
524}
525
526Searcher::CallbackReturn
527CommandCompletions::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
582size_t
583CommandCompletions::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
599static bool
600regex_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}
617CommandCompletions::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
647Searcher::Depth
648CommandCompletions::SymbolCompleter::GetDepth()
649{
650    return eDepthModule;
651}
652
653Searcher::CallbackReturn
654CommandCompletions::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
684size_t
685CommandCompletions::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//----------------------------------------------------------------------
698CommandCompletions::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
713Searcher::Depth
714CommandCompletions::ModuleCompleter::GetDepth()
715{
716    return eDepthModule;
717}
718
719Searcher::CallbackReturn
720CommandCompletions::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
749size_t
750CommandCompletions::ModuleCompleter::DoCompletion (SearchFilter *filter)
751{
752    filter->Search (*this);
753    return m_matches.GetSize();
754}
755