CommandObjectSource.cpp revision 781241147dbdd18762ab676960ecbff18ab0a766
1//===-- CommandObjectSource.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#include "CommandObjectSource.h"
13
14// C Includes
15// C++ Includes
16// Other libraries and framework includes
17// Project includes
18#include "lldb/Interpreter/Args.h"
19#include "lldb/Core/Debugger.h"
20#include "lldb/Core/FileLineResolver.h"
21#include "lldb/Core/Module.h"
22#include "lldb/Core/ModuleSpec.h"
23#include "lldb/Core/SourceManager.h"
24#include "lldb/Interpreter/CommandInterpreter.h"
25#include "lldb/Interpreter/CommandReturnObject.h"
26#include "lldb/Host/FileSpec.h"
27#include "lldb/Symbol/CompileUnit.h"
28#include "lldb/Symbol/Function.h"
29#include "lldb/Target/Process.h"
30#include "lldb/Target/TargetList.h"
31#include "lldb/Interpreter/CommandCompletions.h"
32#include "lldb/Interpreter/Options.h"
33
34using namespace lldb;
35using namespace lldb_private;
36
37//-------------------------------------------------------------------------
38// CommandObjectSourceInfo
39//-------------------------------------------------------------------------
40
41class CommandObjectSourceInfo : public CommandObjectParsed
42{
43
44    class CommandOptions : public Options
45    {
46    public:
47        CommandOptions (CommandInterpreter &interpreter) :
48            Options(interpreter)
49        {
50        }
51
52        ~CommandOptions ()
53        {
54        }
55
56        Error
57        SetOptionValue (uint32_t option_idx, const char *option_arg)
58        {
59            Error error;
60            const int short_option = g_option_table[option_idx].short_option;
61            switch (short_option)
62            {
63            case 'l':
64                start_line = Args::StringToUInt32 (option_arg, 0);
65                if (start_line == 0)
66                    error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg);
67                break;
68
69             case 'f':
70                file_name = option_arg;
71                break;
72
73           default:
74                error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option);
75                break;
76            }
77
78            return error;
79        }
80
81        void
82        OptionParsingStarting ()
83        {
84            file_spec.Clear();
85            file_name.clear();
86            start_line = 0;
87        }
88
89        const OptionDefinition*
90        GetDefinitions ()
91        {
92            return g_option_table;
93        }
94        static OptionDefinition g_option_table[];
95
96        // Instance variables to hold the values for command options.
97        FileSpec file_spec;
98        std::string file_name;
99        uint32_t start_line;
100
101    };
102
103public:
104    CommandObjectSourceInfo(CommandInterpreter &interpreter) :
105        CommandObjectParsed (interpreter,
106                             "source info",
107                             "Display information about the source lines from the current executable's debug info.",
108                             "source info [<cmd-options>]"),
109        m_options (interpreter)
110    {
111    }
112
113    ~CommandObjectSourceInfo ()
114    {
115    }
116
117
118    Options *
119    GetOptions ()
120    {
121        return &m_options;
122    }
123
124protected:
125    bool
126    DoExecute (Args& command, CommandReturnObject &result)
127    {
128        result.AppendError ("Not yet implemented");
129        result.SetStatus (eReturnStatusFailed);
130        return false;
131    }
132
133    CommandOptions m_options;
134};
135
136OptionDefinition
137CommandObjectSourceInfo::CommandOptions::g_option_table[] =
138{
139{ LLDB_OPT_SET_1, false, "line",       'l', required_argument, NULL, 0, eArgTypeLineNum,    "The line number at which to start the display source."},
140{ LLDB_OPT_SET_1, false, "file",       'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename,    "The file from which to display source."},
141{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
142};
143
144#pragma mark CommandObjectSourceList
145//-------------------------------------------------------------------------
146// CommandObjectSourceList
147//-------------------------------------------------------------------------
148
149class CommandObjectSourceList : public CommandObjectParsed
150{
151
152    class CommandOptions : public Options
153    {
154    public:
155        CommandOptions (CommandInterpreter &interpreter) :
156            Options(interpreter)
157        {
158        }
159
160        ~CommandOptions ()
161        {
162        }
163
164        Error
165        SetOptionValue (uint32_t option_idx, const char *option_arg)
166        {
167            Error error;
168            const int short_option = g_option_table[option_idx].short_option;
169            switch (short_option)
170            {
171            case 'l':
172                start_line = Args::StringToUInt32 (option_arg, 0);
173                if (start_line == 0)
174                    error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg);
175                break;
176
177            case 'c':
178                num_lines = Args::StringToUInt32 (option_arg, 0);
179                if (num_lines == 0)
180                    error.SetErrorStringWithFormat("invalid line count: '%s'", option_arg);
181                break;
182
183            case 'f':
184                file_name = option_arg;
185                break;
186
187            case 'n':
188                symbol_name = option_arg;
189                break;
190
191            case 'a':
192                {
193                    ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
194                    address = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error);
195                }
196                break;
197            case 's':
198                modules.push_back (std::string (option_arg));
199                break;
200
201            case 'b':
202                show_bp_locs = true;
203                break;
204           default:
205                error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option);
206                break;
207            }
208
209            return error;
210        }
211
212        void
213        OptionParsingStarting ()
214        {
215            file_spec.Clear();
216            file_name.clear();
217            symbol_name.clear();
218            address = LLDB_INVALID_ADDRESS;
219            start_line = 0;
220            num_lines = 10;
221            show_bp_locs = false;
222            modules.clear();
223        }
224
225        const OptionDefinition*
226        GetDefinitions ()
227        {
228            return g_option_table;
229        }
230        static OptionDefinition g_option_table[];
231
232        // Instance variables to hold the values for command options.
233        FileSpec file_spec;
234        std::string file_name;
235        std::string symbol_name;
236        lldb::addr_t address;
237        uint32_t start_line;
238        uint32_t num_lines;
239        STLStringArray modules;
240        bool show_bp_locs;
241    };
242
243public:
244    CommandObjectSourceList(CommandInterpreter &interpreter) :
245        CommandObjectParsed (interpreter,
246                             "source list",
247                             "Display source code (as specified) based on the current executable's debug info.",
248                             NULL),
249        m_options (interpreter)
250    {
251        CommandArgumentEntry arg;
252        CommandArgumentData file_arg;
253
254        // Define the first (and only) variant of this arg.
255        file_arg.arg_type = eArgTypeFilename;
256        file_arg.arg_repetition = eArgRepeatOptional;
257
258        // There is only one variant this argument could be; put it into the argument entry.
259        arg.push_back (file_arg);
260
261        // Push the data for the first argument into the m_arguments vector.
262        m_arguments.push_back (arg);
263    }
264
265    ~CommandObjectSourceList ()
266    {
267    }
268
269
270    Options *
271    GetOptions ()
272    {
273        return &m_options;
274    }
275
276    virtual const char *
277    GetRepeatCommand (Args &current_command_args, uint32_t index)
278    {
279        return m_cmd_name.c_str();
280    }
281
282protected:
283    bool
284    DoExecute (Args& command, CommandReturnObject &result)
285    {
286        const int argc = command.GetArgumentCount();
287
288        if (argc != 0)
289        {
290            result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", GetCommandName());
291            result.SetStatus (eReturnStatusFailed);
292            return false;
293        }
294
295        ExecutionContext exe_ctx(m_interpreter.GetExecutionContext());
296        Target *target = exe_ctx.GetTargetPtr();
297
298        if (target == NULL)
299            target = m_interpreter.GetDebugger().GetSelectedTarget().get();
300
301        if (target == NULL)
302        {
303            result.AppendError ("invalid target, create a debug target using the 'target create' command");
304            result.SetStatus (eReturnStatusFailed);
305            return false;
306        }
307
308        SymbolContextList sc_list;
309        if (!m_options.symbol_name.empty())
310        {
311            // Displaying the source for a symbol:
312            ConstString name(m_options.symbol_name.c_str());
313            bool include_symbols = false;
314            bool include_inlines = true;
315            bool append = true;
316            size_t num_matches = 0;
317
318            if (m_options.modules.size() > 0)
319            {
320                ModuleList matching_modules;
321                for (unsigned i = 0, e = m_options.modules.size(); i != e; i++)
322                {
323                    FileSpec module_file_spec(m_options.modules[i].c_str(), false);
324                    if (module_file_spec)
325                    {
326                        ModuleSpec module_spec (module_file_spec);
327                        matching_modules.Clear();
328                        target->GetImages().FindModules (module_spec, matching_modules);
329                        num_matches += matching_modules.FindFunctions (name, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list);
330                    }
331                }
332            }
333            else
334            {
335                num_matches = target->GetImages().FindFunctions (name, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list);
336            }
337
338            SymbolContext sc;
339
340            if (num_matches == 0)
341            {
342                result.AppendErrorWithFormat("Could not find function named: \"%s\".\n", m_options.symbol_name.c_str());
343                result.SetStatus (eReturnStatusFailed);
344                return false;
345            }
346
347            sc_list.GetContextAtIndex (0, sc);
348            FileSpec start_file;
349            uint32_t start_line;
350            uint32_t end_line;
351            FileSpec end_file;
352            if (sc.function != NULL)
353            {
354                sc.function->GetStartLineSourceInfo (start_file, start_line);
355                if (start_line == 0)
356                {
357                    result.AppendErrorWithFormat("Could not find line information for start of function: \"%s\".\n", m_options.symbol_name.c_str());
358                    result.SetStatus (eReturnStatusFailed);
359                    return false;
360                }
361                sc.function->GetEndLineSourceInfo (end_file, end_line);
362            }
363            else
364            {
365                result.AppendErrorWithFormat("Could not find function info for: \"%s\".\n", m_options.symbol_name.c_str());
366                result.SetStatus (eReturnStatusFailed);
367                return false;
368            }
369
370            if (num_matches > 1)
371            {
372                // This could either be because there are multiple functions of this name, in which case
373                // we'll have to specify this further...  Or it could be because there are multiple inlined instances
374                // of one function.  So run through the matches and if they all have the same file & line then we can just
375                // list one.
376
377                bool found_multiple = false;
378
379                for (size_t i = 1; i < num_matches; i++)
380                {
381                    SymbolContext scratch_sc;
382                    sc_list.GetContextAtIndex (i, scratch_sc);
383                    if (scratch_sc.function != NULL)
384                    {
385                        FileSpec scratch_file;
386                        uint32_t scratch_line;
387                        scratch_sc.function->GetStartLineSourceInfo (scratch_file, scratch_line);
388                        if (scratch_file != start_file
389                            || scratch_line != start_line)
390                        {
391                            found_multiple = true;
392                            break;
393                        }
394                    }
395                }
396                if (found_multiple)
397                {
398                    StreamString s;
399                    for (size_t i = 0; i < num_matches; i++)
400                    {
401                        SymbolContext scratch_sc;
402                        sc_list.GetContextAtIndex (i, scratch_sc);
403                        if (scratch_sc.function != NULL)
404                        {
405                            s.Printf("\n%lu: ", i);
406                            scratch_sc.function->Dump (&s, true);
407                        }
408                    }
409                    result.AppendErrorWithFormat("Multiple functions found matching: %s: \n%s\n",
410                                                 m_options.symbol_name.c_str(),
411                                                 s.GetData());
412                    result.SetStatus (eReturnStatusFailed);
413                    return false;
414                }
415            }
416
417
418            // This is a little hacky, but the first line table entry for a function points to the "{" that
419            // starts the function block.  It would be nice to actually get the function
420            // declaration in there too.  So back up a bit, but not further than what you're going to display.
421            size_t lines_to_back_up = m_options.num_lines >= 10 ? 5 : m_options.num_lines/2;
422            uint32_t line_no;
423            if (start_line <= lines_to_back_up)
424                line_no = 1;
425            else
426                line_no = start_line - lines_to_back_up;
427
428            // For fun, if the function is shorter than the number of lines we're supposed to display,
429            // only display the function...
430            if (end_line != 0)
431            {
432                if (m_options.num_lines > end_line - line_no)
433                    m_options.num_lines = end_line - line_no;
434            }
435
436            char path_buf[PATH_MAX];
437            start_file.GetPath(path_buf, sizeof(path_buf));
438
439            if (m_options.show_bp_locs)
440            {
441                const bool show_inlines = true;
442                m_breakpoint_locations.Reset (start_file, 0, show_inlines);
443                SearchFilter target_search_filter (exe_ctx.GetTargetSP());
444                target_search_filter.Search (m_breakpoint_locations);
445            }
446            else
447                m_breakpoint_locations.Clear();
448
449            result.AppendMessageWithFormat("File: %s.\n", path_buf);
450            target->GetSourceManager().DisplaySourceLinesWithLineNumbers (start_file,
451                                                                          line_no,
452                                                                          0,
453                                                                          m_options.num_lines,
454                                                                          "",
455                                                                          &result.GetOutputStream(),
456                                                                          GetBreakpointLocations ());
457
458            result.SetStatus (eReturnStatusSuccessFinishResult);
459            return true;
460
461        }
462        else if (m_options.address != LLDB_INVALID_ADDRESS)
463        {
464            SymbolContext sc;
465            Address so_addr;
466            StreamString error_strm;
467
468            if (target->GetSectionLoadList().IsEmpty())
469            {
470                // The target isn't loaded yet, we need to lookup the file address
471                // in all modules
472                const ModuleList &module_list = target->GetImages();
473                const uint32_t num_modules = module_list.GetSize();
474                for (uint32_t i=0; i<num_modules; ++i)
475                {
476                    ModuleSP module_sp (module_list.GetModuleAtIndex(i));
477                    if (module_sp && module_sp->ResolveFileAddress(m_options.address, so_addr))
478                    {
479                        sc.Clear();
480                        if (module_sp->ResolveSymbolContextForAddress (so_addr, eSymbolContextEverything, sc) & eSymbolContextLineEntry)
481                            sc_list.Append(sc);
482                    }
483                }
484
485                if (sc_list.GetSize() == 0)
486                {
487                    result.AppendErrorWithFormat("no modules have source information for file address 0x%" PRIx64 ".\n",
488                                                 m_options.address);
489                    result.SetStatus (eReturnStatusFailed);
490                    return false;
491                }
492            }
493            else
494            {
495                // The target has some things loaded, resolve this address to a
496                // compile unit + file + line and display
497                if (target->GetSectionLoadList().ResolveLoadAddress (m_options.address, so_addr))
498                {
499                    ModuleSP module_sp (so_addr.GetModule());
500                    if (module_sp)
501                    {
502                        sc.Clear();
503                        if (module_sp->ResolveSymbolContextForAddress (so_addr, eSymbolContextEverything, sc) & eSymbolContextLineEntry)
504                        {
505                            sc_list.Append(sc);
506                        }
507                        else
508                        {
509                            so_addr.Dump(&error_strm, NULL, Address::DumpStyleModuleWithFileAddress);
510                            result.AppendErrorWithFormat("address resolves to %s, but there is no line table information available for this address.\n",
511                                                         error_strm.GetData());
512                            result.SetStatus (eReturnStatusFailed);
513                            return false;
514                        }
515                    }
516                }
517
518                if (sc_list.GetSize() == 0)
519                {
520                    result.AppendErrorWithFormat("no modules contain load address 0x%" PRIx64 ".\n", m_options.address);
521                    result.SetStatus (eReturnStatusFailed);
522                    return false;
523                }
524            }
525            uint32_t num_matches = sc_list.GetSize();
526            for (uint32_t i=0; i<num_matches; ++i)
527            {
528                sc_list.GetContextAtIndex(i, sc);
529                if (sc.comp_unit)
530                {
531                    if (m_options.show_bp_locs)
532                    {
533                        m_breakpoint_locations.Clear();
534                        const bool show_inlines = true;
535                        m_breakpoint_locations.Reset (*sc.comp_unit, 0, show_inlines);
536                        SearchFilter target_search_filter (target->shared_from_this());
537                        target_search_filter.Search (m_breakpoint_locations);
538                    }
539
540                    bool show_fullpaths = true;
541                    bool show_module = true;
542                    bool show_inlined_frames = true;
543                    sc.DumpStopContext(&result.GetOutputStream(),
544                                       exe_ctx.GetBestExecutionContextScope(),
545                                       sc.line_entry.range.GetBaseAddress(),
546                                       show_fullpaths,
547                                       show_module,
548                                       show_inlined_frames);
549                    result.GetOutputStream().EOL();
550
551                    size_t lines_to_back_up = m_options.num_lines >= 10 ? 5 : m_options.num_lines/2;
552
553                    target->GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit,
554                                                                                  sc.line_entry.line,
555                                                                                  lines_to_back_up,
556                                                                                  m_options.num_lines - lines_to_back_up,
557                                                                                  "->",
558                                                                                  &result.GetOutputStream(),
559                                                                                  GetBreakpointLocations ());
560                    result.SetStatus (eReturnStatusSuccessFinishResult);
561                }
562            }
563        }
564        else if (m_options.file_name.empty())
565        {
566            // Last valid source manager context, or the current frame if no
567            // valid last context in source manager.
568            // One little trick here, if you type the exact same list command twice in a row, it is
569            // more likely because you typed it once, then typed it again
570            if (m_options.start_line == 0)
571            {
572                if (target->GetSourceManager().DisplayMoreWithLineNumbers (&result.GetOutputStream(),
573                                                                                               GetBreakpointLocations ()))
574                {
575                    result.SetStatus (eReturnStatusSuccessFinishResult);
576                }
577            }
578            else
579            {
580                if (m_options.show_bp_locs)
581                {
582                    SourceManager::FileSP last_file_sp (target->GetSourceManager().GetLastFile ());
583                    if (last_file_sp)
584                    {
585                        const bool show_inlines = true;
586                        m_breakpoint_locations.Reset (last_file_sp->GetFileSpec(), 0, show_inlines);
587                        SearchFilter target_search_filter (target->shared_from_this());
588                        target_search_filter.Search (m_breakpoint_locations);
589                    }
590                }
591                else
592                    m_breakpoint_locations.Clear();
593
594                if (target->GetSourceManager().DisplaySourceLinesWithLineNumbersUsingLastFile(
595                            m_options.start_line,   // Line to display
596                            0,                      // Lines before line to display
597                            m_options.num_lines,    // Lines after line to display
598                            "",                     // Don't mark "line"
599                            &result.GetOutputStream(),
600                            GetBreakpointLocations ()))
601                {
602                    result.SetStatus (eReturnStatusSuccessFinishResult);
603                }
604
605            }
606        }
607        else
608        {
609            const char *filename = m_options.file_name.c_str();
610
611            bool check_inlines = false;
612            SymbolContextList sc_list;
613            size_t num_matches = 0;
614
615            if (m_options.modules.size() > 0)
616            {
617                ModuleList matching_modules;
618                for (unsigned i = 0, e = m_options.modules.size(); i != e; i++)
619                {
620                    FileSpec module_file_spec(m_options.modules[i].c_str(), false);
621                    if (module_file_spec)
622                    {
623                        ModuleSpec module_spec (module_file_spec);
624                        matching_modules.Clear();
625                        target->GetImages().FindModules (module_spec, matching_modules);
626                        num_matches += matching_modules.ResolveSymbolContextForFilePath (filename,
627                                                                                         0,
628                                                                                         check_inlines,
629                                                                                         eSymbolContextModule | eSymbolContextCompUnit,
630                                                                                         sc_list);
631                    }
632                }
633            }
634            else
635            {
636                num_matches = target->GetImages().ResolveSymbolContextForFilePath (filename,
637                                                                                   0,
638                                                                                   check_inlines,
639                                                                                   eSymbolContextModule | eSymbolContextCompUnit,
640                                                                                   sc_list);
641            }
642
643            if (num_matches == 0)
644            {
645                result.AppendErrorWithFormat("Could not find source file \"%s\".\n",
646                                             m_options.file_name.c_str());
647                result.SetStatus (eReturnStatusFailed);
648                return false;
649            }
650
651            if (num_matches > 1)
652            {
653                SymbolContext sc;
654                bool got_multiple = false;
655                FileSpec *test_cu_spec = NULL;
656
657                for (unsigned i = 0; i < num_matches; i++)
658                {
659                    sc_list.GetContextAtIndex(i, sc);
660                    if (sc.comp_unit)
661                    {
662                        if (test_cu_spec)
663                        {
664                            if (test_cu_spec != static_cast<FileSpec *> (sc.comp_unit))
665                                got_multiple = true;
666                                break;
667                        }
668                        else
669                            test_cu_spec = sc.comp_unit;
670                    }
671                }
672                if (got_multiple)
673                {
674                    result.AppendErrorWithFormat("Multiple source files found matching: \"%s.\"\n",
675                                                 m_options.file_name.c_str());
676                    result.SetStatus (eReturnStatusFailed);
677                    return false;
678                }
679            }
680
681            SymbolContext sc;
682            if (sc_list.GetContextAtIndex(0, sc))
683            {
684                if (sc.comp_unit)
685                {
686                    if (m_options.show_bp_locs)
687                    {
688                        const bool show_inlines = true;
689                        m_breakpoint_locations.Reset (*sc.comp_unit, 0, show_inlines);
690                        SearchFilter target_search_filter (target->shared_from_this());
691                        target_search_filter.Search (m_breakpoint_locations);
692                    }
693                    else
694                        m_breakpoint_locations.Clear();
695
696                    target->GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit,
697                                                                                  m_options.start_line,
698                                                                                  0,
699                                                                                  m_options.num_lines,
700                                                                                  "",
701                                                                                  &result.GetOutputStream(),
702                                                                                  GetBreakpointLocations ());
703
704                    result.SetStatus (eReturnStatusSuccessFinishResult);
705                }
706                else
707                {
708                    result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n",
709                                                 m_options.file_name.c_str());
710                    result.SetStatus (eReturnStatusFailed);
711                    return false;
712                }
713            }
714        }
715        return result.Succeeded();
716    }
717
718    const SymbolContextList *
719    GetBreakpointLocations ()
720    {
721        if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0)
722            return &m_breakpoint_locations.GetFileLineMatches();
723        return NULL;
724    }
725    CommandOptions m_options;
726    FileLineResolver m_breakpoint_locations;
727
728};
729
730OptionDefinition
731CommandObjectSourceList::CommandOptions::g_option_table[] =
732{
733{ LLDB_OPT_SET_ALL, false, "count",  'c', required_argument, NULL, 0, eArgTypeCount,   "The number of source lines to display."},
734{ LLDB_OPT_SET_1  |
735  LLDB_OPT_SET_2  , false, "shlib",  's', required_argument, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Look up the source file in the given shared library."},
736{ LLDB_OPT_SET_ALL, false, "show-breakpoints", 'b', no_argument, NULL, 0, eArgTypeNone, "Show the line table locations from the debug information that indicate valid places to set source level breakpoints."},
737{ LLDB_OPT_SET_1  , false, "file",   'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename,    "The file from which to display source."},
738{ LLDB_OPT_SET_1  , false, "line",   'l', required_argument, NULL, 0, eArgTypeLineNum,    "The line number at which to start the display source."},
739{ LLDB_OPT_SET_2  , false, "name",   'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeSymbol,    "The name of a function whose source to display."},
740{ LLDB_OPT_SET_3  , false, "address",'a', required_argument, NULL, 0, eArgTypeAddress, "Lookup the address and display the source information for the corresponding file and line."},
741{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
742};
743
744#pragma mark CommandObjectMultiwordSource
745
746//-------------------------------------------------------------------------
747// CommandObjectMultiwordSource
748//-------------------------------------------------------------------------
749
750CommandObjectMultiwordSource::CommandObjectMultiwordSource (CommandInterpreter &interpreter) :
751    CommandObjectMultiword (interpreter,
752                            "source",
753                            "A set of commands for accessing source file information",
754                            "source <subcommand> [<subcommand-options>]")
755{
756    LoadSubCommand ("info",   CommandObjectSP (new CommandObjectSourceInfo (interpreter)));
757    LoadSubCommand ("list",   CommandObjectSP (new CommandObjectSourceList (interpreter)));
758}
759
760CommandObjectMultiwordSource::~CommandObjectMultiwordSource ()
761{
762}
763
764