blob: 9a3a8bf0966947362626a1505cb3c231b58a0d89 [file] [log] [blame]
Chris Lattner24943d22010-06-08 16:52:24 +00001//===-- CommandCompletions.cpp ----------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10
11// C Includes
Jim Ingham802f8b02010-06-30 05:02:46 +000012#include <sys/stat.h>
13#include <dirent.h>
14#include <libgen.h>
15#include <glob.h>
16
Chris Lattner24943d22010-06-08 16:52:24 +000017// C++ Includes
18// Other libraries and framework includes
19// Project includes
Jim Ingham84cdc152010-06-15 19:49:27 +000020#include "lldb/Interpreter/Args.h"
Chris Lattner24943d22010-06-08 16:52:24 +000021#include "lldb/Interpreter/CommandInterpreter.h"
22#include "lldb/Core/FileSpecList.h"
23#include "lldb/Target/Target.h"
24#include "lldb/Interpreter/CommandCompletions.h"
25
26
27using namespace lldb_private;
28
29CommandCompletions::CommonCompletionElement
30CommandCompletions::g_common_completions[] =
31{
Jim Ingham802f8b02010-06-30 05:02:46 +000032 {eCustomCompletion, NULL},
33 {eSourceFileCompletion, CommandCompletions::SourceFiles},
34 {eDiskFileCompletion, CommandCompletions::DiskFiles},
35 {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories},
36 {eSymbolCompletion, CommandCompletions::Symbols},
37 {eModuleCompletion, CommandCompletions::Modules},
38 {eNoCompletion, NULL} // This one has to be last in the list.
Chris Lattner24943d22010-06-08 16:52:24 +000039};
40
41bool
Greg Clayton63094e02010-06-23 01:19:29 +000042CommandCompletions::InvokeCommonCompletionCallbacks
43(
44 CommandInterpreter &interpreter,
45 uint32_t completion_mask,
46 const char *completion_str,
47 int match_start_point,
48 int max_return_elements,
49 SearchFilter *searcher,
Jim Ingham802f8b02010-06-30 05:02:46 +000050 bool &word_complete,
Greg Clayton63094e02010-06-23 01:19:29 +000051 StringList &matches
52)
Chris Lattner24943d22010-06-08 16:52:24 +000053{
54 bool handled = false;
55
56 if (completion_mask & eCustomCompletion)
57 return false;
58
59 for (int i = 0; ; i++)
60 {
61 if (g_common_completions[i].type == eNoCompletion)
62 break;
63 else if ((g_common_completions[i].type & completion_mask) == g_common_completions[i].type
64 && g_common_completions[i].callback != NULL)
65 {
66 handled = true;
Greg Clayton63094e02010-06-23 01:19:29 +000067 g_common_completions[i].callback (interpreter,
68 completion_str,
Chris Lattner24943d22010-06-08 16:52:24 +000069 match_start_point,
70 max_return_elements,
Chris Lattner24943d22010-06-08 16:52:24 +000071 searcher,
Jim Ingham802f8b02010-06-30 05:02:46 +000072 word_complete,
Chris Lattner24943d22010-06-08 16:52:24 +000073 matches);
74 }
75 }
76 return handled;
77}
78
79int
Greg Clayton63094e02010-06-23 01:19:29 +000080CommandCompletions::SourceFiles
81(
82 CommandInterpreter &interpreter,
83 const char *partial_file_name,
84 int match_start_point,
85 int max_return_elements,
86 SearchFilter *searcher,
Jim Ingham802f8b02010-06-30 05:02:46 +000087 bool &word_complete,
Greg Clayton63094e02010-06-23 01:19:29 +000088 StringList &matches
89)
Chris Lattner24943d22010-06-08 16:52:24 +000090{
Jim Ingham802f8b02010-06-30 05:02:46 +000091 word_complete = true;
Chris Lattner24943d22010-06-08 16:52:24 +000092 // Find some way to switch "include support files..."
Greg Clayton63094e02010-06-23 01:19:29 +000093 SourceFileCompleter completer (interpreter,
94 false,
95 partial_file_name,
96 match_start_point,
97 max_return_elements,
Chris Lattner24943d22010-06-08 16:52:24 +000098 matches);
99
100 if (searcher == NULL)
101 {
Greg Clayton63094e02010-06-23 01:19:29 +0000102 lldb::TargetSP target_sp = interpreter.GetDebugger().GetCurrentTarget();
Chris Lattner24943d22010-06-08 16:52:24 +0000103 SearchFilter null_searcher (target_sp);
104 completer.DoCompletion (&null_searcher);
105 }
106 else
107 {
108 completer.DoCompletion (searcher);
109 }
110 return matches.GetSize();
111}
112
Jim Ingham802f8b02010-06-30 05:02:46 +0000113static int
114DiskFilesOrDirectories
115(
116 const char *partial_file_name,
117 bool only_directories,
118 bool &saw_directory,
119 StringList &matches
120)
121{
122 // I'm going to use the "glob" function with GLOB_TILDE for user directory expansion.
123 // If it is not defined on your host system, you'll need to implement it yourself...
124
125 int partial_name_len = strlen(partial_file_name);
126
127 if (partial_name_len >= PATH_MAX)
128 return matches.GetSize();
129
130 // This copy of the string will be cut up into the directory part, and the remainder. end_ptr
131 // below will point to the place of the remainder in this string. Then when we've resolved the
132 // containing directory, and opened it, we'll read the directory contents and overwrite the
133 // partial_name_copy starting from end_ptr with each of the matches. Thus we will preserve
134 // the form the user originally typed.
135
136 char partial_name_copy[PATH_MAX];
137 bcopy (partial_file_name, partial_name_copy, partial_name_len);
138 partial_name_copy[partial_name_len] = '\0';
139
140 // We'll need to save a copy of the remainder for comparision, which we do here.
141 char remainder[PATH_MAX];
142
143 // end_ptr will point past the last / in partial_name_copy, or if there is no slash to the beginning of the string.
144 char *end_ptr;
145
146 end_ptr = strrchr(partial_name_copy, '/');
147
148 // This will store the resolved form of the containing directory
149 char containing_part[PATH_MAX];
150
151 if (end_ptr == NULL)
152 {
153 // There's no directory. If the thing begins with a "~" then this is a bare
154 // user name.
155 if (*partial_name_copy == '~')
156 {
157 // Nothing here but the user name. We could just put a slash on the end,
158 // but for completeness sake we'll glob the user name and only put a slash
159 // on the end if it exists.
160 glob_t glob_buf;
161 if (glob (partial_name_copy, GLOB_TILDE, NULL, &glob_buf) != 0)
162 return matches.GetSize();
163
164 //The thing exists, put a '/' on the end, and return it...
165 // FIXME: complete user names here:
166 partial_name_copy[partial_name_len] = '/';
167 partial_name_copy[partial_name_len+1] = '\0';
168 matches.AppendString(partial_name_copy);
169 globfree(&glob_buf);
170 saw_directory == true;
171 return matches.GetSize();
172 }
173 else
174 {
175 // The containing part is the CWD, and the whole string is the remainder.
176 containing_part[0] = '.';
177 containing_part[1] = '\0';
178 strcpy(remainder, partial_name_copy);
179 end_ptr = partial_name_copy;
180 }
181 }
182 else
183 {
184 if (end_ptr == partial_name_copy)
185 {
186 // We're completing a file or directory in the root volume.
187 containing_part[0] = '/';
188 containing_part[1] = '\0';
189 }
190 else
191 {
192 size_t len = end_ptr - partial_name_copy;
193 memcpy(containing_part, partial_name_copy, len);
194 containing_part[len] = '\0';
195 }
196 // Push end_ptr past the final "/" and set remainder.
197 end_ptr++;
198 strcpy(remainder, end_ptr);
199 }
200
201 // Look for a user name in the containing part, and if it's there, glob containing_part and stick the
202 // result back into the containing_part:
203
204 if (*partial_name_copy == '~')
205 {
206 glob_t glob_buf;
207
208 // User name doesn't exist, we're not getting any further...
209 if (glob (containing_part, GLOB_TILDE, NULL, &glob_buf) != 0)
210 return matches.GetSize();
211
212 if (glob_buf.gl_pathc != 1)
213 {
214 // I'm not really sure how this would happen?
215 globfree(&glob_buf);
216 return matches.GetSize();
217 }
218 strcpy(containing_part, glob_buf.gl_pathv[0]);
219 globfree(&glob_buf);
220 }
221
222 // Okay, containing_part is now the directory we want to open and look for files:
223
224 DIR *dir_stream;
225
226 dir_stream = opendir(containing_part);
227 if (dir_stream == NULL)
228 return matches.GetSize();
229
230 struct dirent *dirent_buf;
231
232 size_t baselen = end_ptr - partial_name_copy;
233
234 while ((dirent_buf = readdir(dir_stream)) != NULL)
235 {
236 char *name = dirent_buf->d_name;
237
238 // Omit ".", ".." and any . files if the match string doesn't start with .
239 if (name[0] == '.')
240 {
241 if (name[1] == '\0')
242 continue;
243 else if (name[1] == '.' && name[2] == '\0')
244 continue;
245 else if (remainder[0] != '.')
246 continue;
247 }
248
249 if (remainder[0] == '\0' || strstr(dirent_buf->d_name, remainder) == name)
250 {
251 if (strlen(name) + baselen >= PATH_MAX)
252 continue;
253
254 strcpy(end_ptr, name);
255
256 bool isa_directory = false;
257 if (dirent_buf->d_type & DT_DIR)
258 isa_directory = true;
259 else if (dirent_buf->d_type & DT_LNK)
260 {
261 struct stat stat_buf;
262 if ((stat(partial_name_copy, &stat_buf) == 0) && (stat_buf.st_mode & S_IFDIR))
263 isa_directory = true;
264 }
265
266 if (isa_directory)
267 {
268 saw_directory = true;
269 size_t len = strlen(partial_name_copy);
270 partial_name_copy[len] = '/';
271 partial_name_copy[len + 1] = '\0';
272 }
273 if (only_directories && !isa_directory)
274 continue;
275 matches.AppendString(partial_name_copy);
276 }
277 }
278
279 return matches.GetSize();
280}
281
282int
283CommandCompletions::DiskFiles
284(
285 CommandInterpreter &interpreter,
286 const char *partial_file_name,
287 int match_start_point,
288 int max_return_elements,
289 SearchFilter *searcher,
290 bool &word_complete,
291 StringList &matches
292)
293{
294
295 int ret_val = DiskFilesOrDirectories (partial_file_name,
296 false,
297 word_complete,
298 matches);
299 word_complete = !word_complete;
300 return ret_val;
301}
302
303int
304CommandCompletions::DiskDirectories
305(
306 CommandInterpreter &interpreter,
307 const char *partial_file_name,
308 int match_start_point,
309 int max_return_elements,
310 SearchFilter *searcher,
311 bool &word_complete,
312 StringList &matches
313)
314{
315 int ret_val = DiskFilesOrDirectories (partial_file_name,
316 true,
317 word_complete,
318 matches);
319 word_complete = false;
320 return ret_val;
321}
322
Chris Lattner24943d22010-06-08 16:52:24 +0000323int
Greg Clayton63094e02010-06-23 01:19:29 +0000324CommandCompletions::Modules
325(
326 CommandInterpreter &interpreter,
327 const char *partial_file_name,
328 int match_start_point,
329 int max_return_elements,
330 SearchFilter *searcher,
Jim Ingham802f8b02010-06-30 05:02:46 +0000331 bool &word_complete,
Greg Clayton63094e02010-06-23 01:19:29 +0000332 StringList &matches
333)
Chris Lattner24943d22010-06-08 16:52:24 +0000334{
Jim Ingham802f8b02010-06-30 05:02:46 +0000335 word_complete = true;
Greg Clayton63094e02010-06-23 01:19:29 +0000336 ModuleCompleter completer (interpreter,
337 partial_file_name,
338 match_start_point,
339 max_return_elements,
340 matches);
341
Chris Lattner24943d22010-06-08 16:52:24 +0000342 if (searcher == NULL)
343 {
Greg Clayton63094e02010-06-23 01:19:29 +0000344 lldb::TargetSP target_sp = interpreter.GetDebugger().GetCurrentTarget();
Chris Lattner24943d22010-06-08 16:52:24 +0000345 SearchFilter null_searcher (target_sp);
346 completer.DoCompletion (&null_searcher);
347 }
348 else
349 {
350 completer.DoCompletion (searcher);
351 }
352 return matches.GetSize();
353}
354
355int
Greg Clayton63094e02010-06-23 01:19:29 +0000356CommandCompletions::Symbols
357(
358 CommandInterpreter &interpreter,
359 const char *partial_file_name,
360 int match_start_point,
361 int max_return_elements,
362 SearchFilter *searcher,
Jim Ingham802f8b02010-06-30 05:02:46 +0000363 bool &word_complete,
Greg Clayton63094e02010-06-23 01:19:29 +0000364 StringList &matches)
Chris Lattner24943d22010-06-08 16:52:24 +0000365{
Jim Ingham802f8b02010-06-30 05:02:46 +0000366 word_complete = true;
Greg Clayton63094e02010-06-23 01:19:29 +0000367 SymbolCompleter completer (interpreter,
368 partial_file_name,
369 match_start_point,
370 max_return_elements,
371 matches);
Chris Lattner24943d22010-06-08 16:52:24 +0000372
373 if (searcher == NULL)
374 {
Greg Clayton63094e02010-06-23 01:19:29 +0000375 lldb::TargetSP target_sp = interpreter.GetDebugger().GetCurrentTarget();
Chris Lattner24943d22010-06-08 16:52:24 +0000376 SearchFilter null_searcher (target_sp);
377 completer.DoCompletion (&null_searcher);
378 }
379 else
380 {
381 completer.DoCompletion (searcher);
382 }
383 return matches.GetSize();
384}
385
Greg Clayton63094e02010-06-23 01:19:29 +0000386CommandCompletions::Completer::Completer
387(
388 CommandInterpreter &interpreter,
Chris Lattner24943d22010-06-08 16:52:24 +0000389 const char *completion_str,
390 int match_start_point,
391 int max_return_elements,
Chris Lattner24943d22010-06-08 16:52:24 +0000392 StringList &matches
393) :
Greg Clayton63094e02010-06-23 01:19:29 +0000394 m_interpreter (interpreter),
Chris Lattner24943d22010-06-08 16:52:24 +0000395 m_completion_str (completion_str),
396 m_match_start_point (match_start_point),
397 m_max_return_elements (max_return_elements),
Chris Lattner24943d22010-06-08 16:52:24 +0000398 m_matches (matches)
399{
400}
401
402CommandCompletions::Completer::~Completer ()
403{
404
405}
406
407//----------------------------------------------------------------------
408// SourceFileCompleter
409//----------------------------------------------------------------------
410
Greg Clayton63094e02010-06-23 01:19:29 +0000411CommandCompletions::SourceFileCompleter::SourceFileCompleter
412(
413 CommandInterpreter &interpreter,
Chris Lattner24943d22010-06-08 16:52:24 +0000414 bool include_support_files,
415 const char *completion_str,
416 int match_start_point,
417 int max_return_elements,
Chris Lattner24943d22010-06-08 16:52:24 +0000418 StringList &matches
419) :
Greg Clayton63094e02010-06-23 01:19:29 +0000420 CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches),
Chris Lattner24943d22010-06-08 16:52:24 +0000421 m_include_support_files (include_support_files),
422 m_matching_files()
423{
424 FileSpec partial_spec (m_completion_str.c_str());
425 m_file_name = partial_spec.GetFilename().GetCString();
426 m_dir_name = partial_spec.GetDirectory().GetCString();
427}
428
429Searcher::Depth
430CommandCompletions::SourceFileCompleter::GetDepth()
431{
432 return eDepthCompUnit;
433}
434
435Searcher::CallbackReturn
436CommandCompletions::SourceFileCompleter::SearchCallback (
437 SearchFilter &filter,
438 SymbolContext &context,
439 Address *addr,
440 bool complete
441)
442{
443 if (context.comp_unit != NULL)
444 {
445 if (m_include_support_files)
446 {
447 FileSpecList supporting_files = context.comp_unit->GetSupportFiles();
448 for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++)
449 {
450 const FileSpec &sfile_spec = supporting_files.GetFileSpecAtIndex(sfiles);
451 const char *sfile_file_name = sfile_spec.GetFilename().GetCString();
452 const char *sfile_dir_name = sfile_spec.GetFilename().GetCString();
453 bool match = false;
454 if (m_file_name && sfile_file_name
455 && strstr (sfile_file_name, m_file_name) == sfile_file_name)
456 match = true;
457 if (match && m_dir_name && sfile_dir_name
458 && strstr (sfile_dir_name, m_dir_name) != sfile_dir_name)
459 match = false;
460
461 if (match)
462 {
463 m_matching_files.AppendIfUnique(sfile_spec);
464 }
465 }
466
467 }
468 else
469 {
470 const char *cur_file_name = context.comp_unit->GetFilename().GetCString();
471 const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString();
472
473 bool match = false;
474 if (m_file_name && cur_file_name
475 && strstr (cur_file_name, m_file_name) == cur_file_name)
476 match = true;
477
478 if (match && m_dir_name && cur_dir_name
479 && strstr (cur_dir_name, m_dir_name) != cur_dir_name)
480 match = false;
481
482 if (match)
483 {
484 m_matching_files.AppendIfUnique(context.comp_unit);
485 }
486 }
487 }
488 return Searcher::eCallbackReturnContinue;
489}
490
491size_t
492CommandCompletions::SourceFileCompleter::DoCompletion (SearchFilter *filter)
493{
494 filter->Search (*this);
495 // Now convert the filelist to completions:
496 for (size_t i = 0; i < m_matching_files.GetSize(); i++)
497 {
498 m_matches.AppendString (m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());
499 }
500 return m_matches.GetSize();
501
502}
503
504//----------------------------------------------------------------------
505// SymbolCompleter
506//----------------------------------------------------------------------
507
508static bool
509regex_chars (const char comp)
510{
511 if (comp == '[' || comp == ']' || comp == '(' || comp == ')')
512 return true;
513 else
514 return false;
515}
Greg Clayton63094e02010-06-23 01:19:29 +0000516CommandCompletions::SymbolCompleter::SymbolCompleter
517(
518 CommandInterpreter &interpreter,
Chris Lattner24943d22010-06-08 16:52:24 +0000519 const char *completion_str,
520 int match_start_point,
521 int max_return_elements,
Chris Lattner24943d22010-06-08 16:52:24 +0000522 StringList &matches
523) :
Greg Clayton63094e02010-06-23 01:19:29 +0000524 CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches)
Chris Lattner24943d22010-06-08 16:52:24 +0000525{
526 std::string regex_str ("^");
527 regex_str.append(completion_str);
528 regex_str.append(".*");
529 std::string::iterator pos;
530
531 pos = find_if(regex_str.begin(), regex_str.end(), regex_chars);
532 while (pos < regex_str.end()) {
533 pos = regex_str.insert(pos, '\\');
534 pos += 2;
535 pos = find_if(pos, regex_str.end(), regex_chars);
536 }
537 m_regex.Compile(regex_str.c_str());
538}
539
540Searcher::Depth
541CommandCompletions::SymbolCompleter::GetDepth()
542{
543 return eDepthModule;
544}
545
546Searcher::CallbackReturn
547CommandCompletions::SymbolCompleter::SearchCallback (
548 SearchFilter &filter,
549 SymbolContext &context,
550 Address *addr,
551 bool complete
552)
553{
554 SymbolContextList func_list;
555 SymbolContextList sym_list;
556
557 if (context.module_sp != NULL)
558 {
559 if (context.module_sp)
560 {
561 context.module_sp->FindSymbolsMatchingRegExAndType (m_regex, lldb::eSymbolTypeCode, sym_list);
562 context.module_sp->FindFunctions (m_regex, true, func_list);
563 }
564
565 SymbolContext sc;
566 // Now add the functions & symbols to the list - only add if unique:
567 for (int i = 0; i < func_list.GetSize(); i++)
568 {
569 if (func_list.GetContextAtIndex(i, sc))
570 {
571 if (sc.function)
572 {
573 m_match_set.insert (sc.function->GetMangled().GetDemangledName());
574 }
575 }
576 }
577
578 for (int i = 0; i < sym_list.GetSize(); i++)
579 {
580 if (sym_list.GetContextAtIndex(i, sc))
581 {
582 if (sc.symbol && sc.symbol->GetAddressRangePtr())
583 {
584 m_match_set.insert (sc.symbol->GetMangled().GetDemangledName());
585 }
586 }
587 }
588 }
589 return Searcher::eCallbackReturnContinue;
590}
591
592size_t
593CommandCompletions::SymbolCompleter::DoCompletion (SearchFilter *filter)
594{
595 filter->Search (*this);
596 collection::iterator pos = m_match_set.begin(), end = m_match_set.end();
597 for (pos = m_match_set.begin(); pos != end; pos++)
598 m_matches.AppendString((*pos).GetCString());
599
600 return m_matches.GetSize();
601}
602
603//----------------------------------------------------------------------
604// ModuleCompleter
605//----------------------------------------------------------------------
Greg Clayton63094e02010-06-23 01:19:29 +0000606CommandCompletions::ModuleCompleter::ModuleCompleter
607(
608 CommandInterpreter &interpreter,
Chris Lattner24943d22010-06-08 16:52:24 +0000609 const char *completion_str,
610 int match_start_point,
611 int max_return_elements,
Chris Lattner24943d22010-06-08 16:52:24 +0000612 StringList &matches
613) :
Greg Clayton63094e02010-06-23 01:19:29 +0000614 CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches)
Chris Lattner24943d22010-06-08 16:52:24 +0000615{
616 FileSpec partial_spec (m_completion_str.c_str());
617 m_file_name = partial_spec.GetFilename().GetCString();
618 m_dir_name = partial_spec.GetDirectory().GetCString();
619}
620
621Searcher::Depth
622CommandCompletions::ModuleCompleter::GetDepth()
623{
624 return eDepthModule;
625}
626
627Searcher::CallbackReturn
628CommandCompletions::ModuleCompleter::SearchCallback (
629 SearchFilter &filter,
630 SymbolContext &context,
631 Address *addr,
632 bool complete
633)
634{
635 if (context.module_sp != NULL)
636 {
637 const char *cur_file_name = context.module_sp->GetFileSpec().GetFilename().GetCString();
638 const char *cur_dir_name = context.module_sp->GetFileSpec().GetDirectory().GetCString();
639
640 bool match = false;
641 if (m_file_name && cur_file_name
642 && strstr (cur_file_name, m_file_name) == cur_file_name)
643 match = true;
644
645 if (match && m_dir_name && cur_dir_name
646 && strstr (cur_dir_name, m_dir_name) != cur_dir_name)
647 match = false;
648
649 if (match)
650 {
651 m_matches.AppendString (cur_file_name);
652 }
653 }
654 return Searcher::eCallbackReturnContinue;
655}
656
657size_t
658CommandCompletions::ModuleCompleter::DoCompletion (SearchFilter *filter)
659{
660 filter->Search (*this);
661 return m_matches.GetSize();
662}
663
664
665