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