CommandObjectExpression.cpp revision 258054
190075Sobrien//===-- CommandObjectExpression.cpp -----------------------------*- C++ -*-===//
290075Sobrien//
3132718Skan//                     The LLVM Compiler Infrastructure
490075Sobrien//
590075Sobrien// This file is distributed under the University of Illinois Open Source
690075Sobrien// License. See LICENSE.TXT for details.
790075Sobrien//
890075Sobrien//===----------------------------------------------------------------------===//
990075Sobrien
1090075Sobrien#include "lldb/lldb-python.h"
1190075Sobrien
1290075Sobrien#include "CommandObjectExpression.h"
1390075Sobrien
1490075Sobrien// C Includes
1590075Sobrien// C++ Includes
1690075Sobrien// Other libraries and framework includes
1790075Sobrien// Project includes
1890075Sobrien#include "lldb/Interpreter/Args.h"
1990075Sobrien#include "lldb/Core/Value.h"
2090075Sobrien#include "lldb/Core/InputReader.h"
2190075Sobrien#include "lldb/Core/ValueObjectVariable.h"
2290075Sobrien#include "lldb/DataFormatters/ValueObjectPrinter.h"
2390075Sobrien#include "lldb/Expression/ClangExpressionVariable.h"
2490075Sobrien#include "lldb/Expression/ClangUserExpression.h"
25132718Skan#include "lldb/Expression/ClangFunction.h"
26132718Skan#include "lldb/Expression/DWARFExpression.h"
2790075Sobrien#include "lldb/Host/Host.h"
28117395Skan#include "lldb/Core/Debugger.h"
2990075Sobrien#include "lldb/Interpreter/CommandInterpreter.h"
3090075Sobrien#include "lldb/Interpreter/CommandReturnObject.h"
3190075Sobrien#include "lldb/Target/ObjCLanguageRuntime.h"
3290075Sobrien#include "lldb/Symbol/ObjectFile.h"
3390075Sobrien#include "lldb/Symbol/Variable.h"
3490075Sobrien#include "lldb/Target/Process.h"
3590075Sobrien#include "lldb/Target/StackFrame.h"
3690075Sobrien#include "lldb/Target/Target.h"
3790075Sobrien#include "lldb/Target/Thread.h"
3890075Sobrien#include "llvm/ADT/StringRef.h"
3990075Sobrien
4090075Sobrienusing namespace lldb;
41132718Skanusing namespace lldb_private;
4290075Sobrien
4390075SobrienCommandObjectExpression::CommandOptions::CommandOptions () :
4490075Sobrien    OptionGroup()
4590075Sobrien{
4690075Sobrien}
4790075Sobrien
4890075Sobrien
4990075SobrienCommandObjectExpression::CommandOptions::~CommandOptions ()
5090075Sobrien{
5190075Sobrien}
52132718Skan
53132718Skanstatic OptionEnumValueElement g_description_verbosity_type[] =
54132718Skan{
55132718Skan    { eLanguageRuntimeDescriptionDisplayVerbosityCompact,      "compact",       "Only show the description string"},
56132718Skan    { eLanguageRuntimeDescriptionDisplayVerbosityFull,         "full",          "Show the full output, including persistent variable's name and type"},
57132718Skan    { 0, NULL, NULL }
58132718Skan};
59132718Skan
60132718SkanOptionDefinition
6190075SobrienCommandObjectExpression::CommandOptions::g_option_table[] =
6290075Sobrien{
6390075Sobrien    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "all-threads",        'a', OptionParser::eRequiredArgument, NULL, 0, eArgTypeBoolean,    "Should we run all threads if the execution doesn't complete on one thread."},
6490075Sobrien    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "ignore-breakpoints", 'i', OptionParser::eRequiredArgument, NULL, 0, eArgTypeBoolean,    "Ignore breakpoint hits while running expressions"},
65132718Skan    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "timeout",            't', OptionParser::eRequiredArgument, NULL, 0, eArgTypeUnsignedInteger,  "Timeout value (in microseconds) for running the expression."},
6690075Sobrien    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "unwind-on-error",    'u', OptionParser::eRequiredArgument, NULL, 0, eArgTypeBoolean,    "Clean up program state if the expression causes a crash, or raises a signal.  Note, unlike gdb hitting a breakpoint is controlled by another option (-i)."},
67117395Skan    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "debug",              'g', OptionParser::eNoArgument      , NULL, 0, eArgTypeNone,       "When specified, debug the JIT code by setting a breakpoint on the first instruction and forcing breakpoints to not be ignored (-i0) and no unwinding to happen on error (-u0)."},
6890075Sobrien    { LLDB_OPT_SET_1, false, "description-verbosity", 'v', OptionParser::eOptionalArgument, g_description_verbosity_type, 0, eArgTypeDescriptionVerbosity,        "How verbose should the output of this expression be, if the object description is asked for."},
6990075Sobrien};
7090075Sobrien
71117395Skan
7290075Sobrienuint32_t
7390075SobrienCommandObjectExpression::CommandOptions::GetNumDefinitions ()
7490075Sobrien{
75132718Skan    return sizeof(g_option_table)/sizeof(OptionDefinition);
76132718Skan}
7790075Sobrien
7890075SobrienError
7990075SobrienCommandObjectExpression::CommandOptions::SetOptionValue (CommandInterpreter &interpreter,
8090075Sobrien                                                         uint32_t option_idx,
8190075Sobrien                                                         const char *option_arg)
8290075Sobrien{
8390075Sobrien    Error error;
8490075Sobrien
8590075Sobrien    const int short_option = g_option_table[option_idx].short_option;
8690075Sobrien
8790075Sobrien    switch (short_option)
8890075Sobrien    {
8990075Sobrien      //case 'l':
9096263Sobrien      //if (language.SetLanguageFromCString (option_arg) == false)
9196263Sobrien      //{
9290075Sobrien      //    error.SetErrorStringWithFormat("invalid language option argument '%s'", option_arg);
9396263Sobrien      //}
9490075Sobrien      //break;
9596263Sobrien
9696263Sobrien    case 'a':
9796263Sobrien        {
9890075Sobrien            bool success;
9996263Sobrien            bool result;
10096263Sobrien            result = Args::StringToBoolean(option_arg, true, &success);
10190075Sobrien            if (!success)
10290075Sobrien                error.SetErrorStringWithFormat("invalid all-threads value setting: \"%s\"", option_arg);
10390075Sobrien            else
10490075Sobrien                try_all_threads = result;
10590075Sobrien        }
106132718Skan        break;
10790075Sobrien
108132718Skan    case 'i':
10990075Sobrien        {
11090075Sobrien            bool success;
111132718Skan            bool tmp_value = Args::StringToBoolean(option_arg, true, &success);
112132718Skan            if (success)
113132718Skan                ignore_breakpoints = tmp_value;
114132718Skan            else
115132718Skan                error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg);
116132718Skan            break;
117132718Skan        }
118132718Skan    case 't':
119132718Skan        {
120132718Skan            bool success;
121132718Skan            uint32_t result;
122132718Skan            result = Args::StringToUInt32(option_arg, 0, 0, &success);
123132718Skan            if (success)
124132718Skan                timeout = result;
125132718Skan            else
126132718Skan                error.SetErrorStringWithFormat ("invalid timeout setting \"%s\"", option_arg);
127132718Skan        }
128132718Skan        break;
129132718Skan
130132718Skan    case 'u':
131132718Skan        {
132132718Skan            bool success;
13390075Sobrien            bool tmp_value = Args::StringToBoolean(option_arg, true, &success);
13490075Sobrien            if (success)
135132718Skan                unwind_on_error = tmp_value;
136132718Skan            else
137132718Skan                error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg);
138117395Skan            break;
139117395Skan        }
140117395Skan
14190075Sobrien    case 'v':
14290075Sobrien        if (!option_arg)
14390075Sobrien        {
14490075Sobrien            m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityFull;
14590075Sobrien            break;
14690075Sobrien        }
14790075Sobrien        m_verbosity = (LanguageRuntimeDescriptionDisplayVerbosity) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error);
14890075Sobrien        if (!error.Success())
14990075Sobrien            error.SetErrorStringWithFormat ("unrecognized value for description-verbosity '%s'", option_arg);
15090075Sobrien        break;
15190075Sobrien
15290075Sobrien    case 'g':
15390075Sobrien        debug = true;
15490075Sobrien        unwind_on_error = false;
15590075Sobrien        ignore_breakpoints = false;
15690075Sobrien        break;
15790075Sobrien
15890075Sobrien    default:
15990075Sobrien        error.SetErrorStringWithFormat("invalid short option character '%c'", short_option);
16090075Sobrien        break;
16190075Sobrien    }
16290075Sobrien
16390075Sobrien    return error;
164132718Skan}
165132718Skan
16690075Sobrienvoid
16790075SobrienCommandObjectExpression::CommandOptions::OptionParsingStarting (CommandInterpreter &interpreter)
16890075Sobrien{
16990075Sobrien    Process *process = interpreter.GetExecutionContext().GetProcessPtr();
17090075Sobrien    if (process != NULL)
17190075Sobrien    {
17290075Sobrien        ignore_breakpoints = process->GetIgnoreBreakpointsInExpressions();
17390075Sobrien        unwind_on_error    = process->GetUnwindOnErrorInExpressions();
17490075Sobrien    }
17590075Sobrien    else
17690075Sobrien    {
17790075Sobrien        ignore_breakpoints = false;
17890075Sobrien        unwind_on_error = true;
179132718Skan    }
180132718Skan
18190075Sobrien    show_summary = true;
18290075Sobrien    try_all_threads = true;
18390075Sobrien    timeout = 0;
18490075Sobrien    debug = false;
18590075Sobrien    m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityCompact;
18690075Sobrien}
18790075Sobrien
18890075Sobrienconst OptionDefinition*
18990075SobrienCommandObjectExpression::CommandOptions::GetDefinitions ()
19090075Sobrien{
19190075Sobrien    return g_option_table;
19290075Sobrien}
19390075Sobrien
19490075SobrienCommandObjectExpression::CommandObjectExpression (CommandInterpreter &interpreter) :
19590075Sobrien    CommandObjectRaw (interpreter,
19690075Sobrien                      "expression",
197132718Skan                      "Evaluate a C/ObjC/C++ expression in the current program context, using user defined variables and variables currently in scope.",
198132718Skan                      NULL,
19990075Sobrien                      eFlagProcessMustBePaused | eFlagTryTargetAPILock),
20090075Sobrien    m_option_group (interpreter),
20190075Sobrien    m_format_options (eFormatDefault),
202132718Skan    m_command_options (),
203132718Skan    m_expr_line_count (0),
204132718Skan    m_expr_lines ()
205132718Skan{
20690075Sobrien  SetHelpLong(
207146895Skan"Timeouts:\n\
208146895Skan    If the expression can be evaluated statically (without runnning code) then it will be.\n\
209132718Skan    Otherwise, by default the expression will run on the current thread with a short timeout:\n\
210132718Skan    currently .25 seconds.  If it doesn't return in that time, the evaluation will be interrupted\n\
211132718Skan    and resumed with all threads running.  You can use the -a option to disable retrying on all\n\
212132718Skan    threads.  You can use the -t option to set a shorter timeout.\n\
213132718Skan\n\
214132718SkanUser defined variables:\n\
215132718Skan    You can define your own variables for convenience or to be used in subsequent expressions.\n\
216132718Skan    You define them the same way you would define variables in C.  If the first character of \n\
217132718Skan    your user defined variable is a $, then the variable's value will be available in future\n\
218132718Skan    expressions, otherwise it will just be available in the current expression.\n\
219132718Skan\n\
220132718SkanExamples: \n\
221132718Skan\n\
222132718Skan   expr my_struct->a = my_array[3] \n\
223132718Skan   expr -f bin -- (index * 8) + 5 \n\
224132718Skan   expr unsigned int $foo = 5\n\
225132718Skan   expr char c[] = \"foo\"; c[0]\n");
226132718Skan
227132718Skan    CommandArgumentEntry arg;
228132718Skan    CommandArgumentData expression_arg;
229132718Skan
230117395Skan    // Define the first (and only) variant of this arg.
231132718Skan    expression_arg.arg_type = eArgTypeExpression;
232132718Skan    expression_arg.arg_repetition = eArgRepeatPlain;
233117395Skan
234117395Skan    // There is only one variant this argument could be; put it into the argument entry.
235117395Skan    arg.push_back (expression_arg);
236117395Skan
237132718Skan    // Push the data for the first argument into the m_arguments vector.
238132718Skan    m_arguments.push_back (arg);
239117395Skan
240117395Skan    // Add the "--format" and "--gdb-format"
241132718Skan    m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_1);
242117395Skan    m_option_group.Append (&m_command_options);
243132718Skan    m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
244132718Skan    m_option_group.Finalize();
245132718Skan}
246132718Skan
247132718SkanCommandObjectExpression::~CommandObjectExpression ()
248132718Skan{
249132718Skan}
250132718Skan
251132718SkanOptions *
252132718SkanCommandObjectExpression::GetOptions ()
253132718Skan{
254132718Skan    return &m_option_group;
255132718Skan}
256117395Skan
257117395Skansize_t
258117395SkanCommandObjectExpression::MultiLineExpressionCallback
25990075Sobrien(
26090075Sobrien    void *baton,
26190075Sobrien    InputReader &reader,
26290075Sobrien    lldb::InputReaderAction notification,
26390075Sobrien    const char *bytes,
26490075Sobrien    size_t bytes_len
26590075Sobrien)
266132718Skan{
26790075Sobrien    CommandObjectExpression *cmd_object_expr = (CommandObjectExpression *) baton;
26890075Sobrien    bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode();
26990075Sobrien
270132718Skan    switch (notification)
271132718Skan    {
27290075Sobrien    case eInputReaderActivate:
27390075Sobrien        if (!batch_mode)
27490075Sobrien        {
27590075Sobrien            StreamSP async_strm_sp(reader.GetDebugger().GetAsyncOutputStream());
27690075Sobrien            if (async_strm_sp)
27790075Sobrien            {
27890075Sobrien                async_strm_sp->PutCString("Enter expressions, then terminate with an empty line to evaluate:\n");
27990075Sobrien                async_strm_sp->Flush();
28090075Sobrien            }
28190075Sobrien        }
28290075Sobrien        // Fall through
283132718Skan    case eInputReaderReactivate:
284132718Skan        break;
285132718Skan
286132718Skan    case eInputReaderDeactivate:
287132718Skan        break;
288132718Skan
28990075Sobrien    case eInputReaderAsynchronousOutputWritten:
29090075Sobrien        break;
29190075Sobrien
292132718Skan    case eInputReaderGotToken:
293132718Skan        ++cmd_object_expr->m_expr_line_count;
294132718Skan        if (bytes && bytes_len)
295132718Skan        {
296132718Skan            cmd_object_expr->m_expr_lines.append (bytes, bytes_len + 1);
297132718Skan        }
29890075Sobrien
299132718Skan        if (bytes_len == 0)
300132718Skan            reader.SetIsDone(true);
301132718Skan        break;
302132718Skan
303132718Skan    case eInputReaderInterrupt:
304132718Skan        cmd_object_expr->m_expr_lines.clear();
305132718Skan        reader.SetIsDone (true);
306132718Skan        if (!batch_mode)
30790075Sobrien        {
30890075Sobrien            StreamSP async_strm_sp (reader.GetDebugger().GetAsyncOutputStream());
30990075Sobrien            if (async_strm_sp)
310132718Skan            {
311132718Skan                async_strm_sp->PutCString("Expression evaluation cancelled.\n");
312132718Skan                async_strm_sp->Flush();
313132718Skan            }
314132718Skan        }
315132718Skan        break;
316132718Skan
317132718Skan    case eInputReaderEndOfFile:
318132718Skan        reader.SetIsDone (true);
319132718Skan        break;
32090075Sobrien
32190075Sobrien    case eInputReaderDone:
32290075Sobrien		if (cmd_object_expr->m_expr_lines.size() > 0)
32390075Sobrien        {
32490075Sobrien            StreamSP output_stream = reader.GetDebugger().GetAsyncOutputStream();
325132718Skan            StreamSP error_stream = reader.GetDebugger().GetAsyncErrorStream();
326132718Skan            cmd_object_expr->EvaluateExpression (cmd_object_expr->m_expr_lines.c_str(),
327132718Skan                                                 output_stream.get(),
328132718Skan                                                 error_stream.get());
32990075Sobrien            output_stream->Flush();
33090075Sobrien            error_stream->Flush();
331132718Skan        }
33290075Sobrien        break;
333132718Skan    }
33490075Sobrien
335117395Skan    return bytes_len;
336117395Skan}
337132718Skan
338132718Skanbool
339132718SkanCommandObjectExpression::EvaluateExpression
340132718Skan(
341132718Skan    const char *expr,
342132718Skan    Stream *output_stream,
343117395Skan    Stream *error_stream,
344117395Skan    CommandReturnObject *result
345132718Skan)
346132718Skan{
34790075Sobrien    // Don't use m_exe_ctx as this might be called asynchronously
348117395Skan    // after the command object DoExecute has finished when doing
349117395Skan    // multi-line expression that use an input reader...
350117395Skan    ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
35190075Sobrien
352132718Skan    Target *target = exe_ctx.GetTargetPtr();
353132718Skan
354117395Skan    if (!target)
355117395Skan        target = Host::GetDummyTarget(m_interpreter.GetDebugger()).get();
356117395Skan
35790075Sobrien    if (target)
358117395Skan    {
359117395Skan        lldb::ValueObjectSP result_valobj_sp;
36090075Sobrien
361132718Skan        ExecutionResults exe_results;
36290075Sobrien
363117395Skan        bool keep_in_memory = true;
364132718Skan
36590075Sobrien        EvaluateExpressionOptions options;
366117395Skan        options.SetCoerceToId(m_varobj_options.use_objc)
36790075Sobrien        .SetUnwindOnError(m_command_options.unwind_on_error)
368117395Skan        .SetIgnoreBreakpoints (m_command_options.ignore_breakpoints)
36990075Sobrien        .SetKeepInMemory(keep_in_memory)
37090075Sobrien        .SetUseDynamic(m_varobj_options.use_dynamic)
37190075Sobrien        .SetRunOthers(m_command_options.try_all_threads)
37290075Sobrien        .SetDebug(m_command_options.debug);
373132718Skan
374132718Skan        if (m_command_options.timeout > 0)
37590075Sobrien            options.SetTimeoutUsec(m_command_options.timeout);
37690075Sobrien
37790075Sobrien        exe_results = target->EvaluateExpression (expr,
37890075Sobrien                                                  exe_ctx.GetFramePtr(),
379117395Skan                                                  result_valobj_sp,
380117395Skan                                                  options);
381117395Skan
382117395Skan        if (result_valobj_sp)
383117395Skan        {
384117395Skan            Format format = m_format_options.GetFormat();
385117395Skan
386117395Skan            if (result_valobj_sp->GetError().Success())
387117395Skan            {
388117395Skan                if (format != eFormatVoid)
389117395Skan                {
390117395Skan                    if (format != eFormatDefault)
391117395Skan                        result_valobj_sp->SetFormat (format);
392117395Skan
393117395Skan                    DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(m_command_options.m_verbosity,format));
394117395Skan
395117395Skan                    result_valobj_sp->Dump(*output_stream,options);
396117395Skan
397117395Skan                    if (result)
398117395Skan                        result->SetStatus (eReturnStatusSuccessFinishResult);
399117395Skan                }
400117395Skan            }
401117395Skan            else
402117395Skan            {
403117395Skan                if (result_valobj_sp->GetError().GetError() == ClangUserExpression::kNoResult)
404117395Skan                {
405117395Skan                    if (format != eFormatVoid && m_interpreter.GetDebugger().GetNotifyVoid())
406117395Skan                    {
407117395Skan                        error_stream->PutCString("(void)\n");
408117395Skan                    }
409117395Skan
410117395Skan                    if (result)
411117395Skan                        result->SetStatus (eReturnStatusSuccessFinishResult);
412117395Skan                }
413117395Skan                else
414117395Skan                {
415117395Skan                    const char *error_cstr = result_valobj_sp->GetError().AsCString();
416132718Skan                    if (error_cstr && error_cstr[0])
417132718Skan                    {
418117395Skan                        const size_t error_cstr_len = strlen (error_cstr);
419132718Skan                        const bool ends_with_newline = error_cstr[error_cstr_len - 1] == '\n';
420132718Skan                        if (strstr(error_cstr, "error:") != error_cstr)
421132718Skan                            error_stream->PutCString ("error: ");
422132718Skan                        error_stream->Write(error_cstr, error_cstr_len);
423132718Skan                        if (!ends_with_newline)
424132718Skan                            error_stream->EOL();
425132718Skan                    }
426132718Skan                    else
427132718Skan                    {
428132718Skan                        error_stream->PutCString ("error: unknown error\n");
429132718Skan                    }
430132718Skan
431132718Skan                    if (result)
432132718Skan                        result->SetStatus (eReturnStatusFailed);
433132718Skan                }
434132718Skan            }
435132718Skan        }
436132718Skan    }
437132718Skan    else
438132718Skan    {
439132718Skan        error_stream->Printf ("error: invalid execution context for expression\n");
440132718Skan        return false;
441132718Skan    }
442132718Skan
443132718Skan    return true;
44490075Sobrien}
44590075Sobrien
44690075Sobrienbool
44790075SobrienCommandObjectExpression::DoExecute
44890075Sobrien(
44990075Sobrien    const char *command,
45090075Sobrien    CommandReturnObject &result
45190075Sobrien)
45290075Sobrien{
45390075Sobrien    m_option_group.NotifyOptionParsingStarting();
45490075Sobrien
45590075Sobrien    const char * expr = NULL;
45690075Sobrien
45790075Sobrien    if (command[0] == '\0')
45890075Sobrien    {
45990075Sobrien        m_expr_lines.clear();
46090075Sobrien        m_expr_line_count = 0;
46190075Sobrien
46290075Sobrien        InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger()));
46390075Sobrien        if (reader_sp)
46490075Sobrien        {
46590075Sobrien            Error err (reader_sp->Initialize (CommandObjectExpression::MultiLineExpressionCallback,
46690075Sobrien                                              this,                         // baton
46790075Sobrien                                              eInputReaderGranularityLine,  // token size, to pass to callback function
46890075Sobrien                                              NULL,                         // end token
46990075Sobrien                                              NULL,                         // prompt
47090075Sobrien                                              true));                       // echo input
47190075Sobrien            if (err.Success())
47290075Sobrien            {
47390075Sobrien                m_interpreter.GetDebugger().PushInputReader (reader_sp);
47490075Sobrien                result.SetStatus (eReturnStatusSuccessFinishNoResult);
47590075Sobrien            }
47690075Sobrien            else
47790075Sobrien            {
47890075Sobrien                result.AppendError (err.AsCString());
47990075Sobrien                result.SetStatus (eReturnStatusFailed);
48090075Sobrien            }
48190075Sobrien        }
48290075Sobrien        else
48390075Sobrien        {
48490075Sobrien            result.AppendError("out of memory");
48590075Sobrien            result.SetStatus (eReturnStatusFailed);
48690075Sobrien        }
48790075Sobrien        return result.Succeeded();
48890075Sobrien    }
48990075Sobrien
49090075Sobrien    if (command[0] == '-')
49190075Sobrien    {
49290075Sobrien        // We have some options and these options MUST end with --.
49390075Sobrien        const char *end_options = NULL;
49490075Sobrien        const char *s = command;
49590075Sobrien        while (s && s[0])
49690075Sobrien        {
49790075Sobrien            end_options = ::strstr (s, "--");
49890075Sobrien            if (end_options)
49990075Sobrien            {
50090075Sobrien                end_options += 2; // Get past the "--"
50190075Sobrien                if (::isspace (end_options[0]))
50290075Sobrien                {
50390075Sobrien                    expr = end_options;
50490075Sobrien                    while (::isspace (*expr))
50590075Sobrien                        ++expr;
50690075Sobrien                    break;
50790075Sobrien                }
50890075Sobrien            }
50990075Sobrien            s = end_options;
51090075Sobrien        }
51190075Sobrien
51290075Sobrien        if (end_options)
51390075Sobrien        {
51490075Sobrien            Args args (command, end_options - command);
51590075Sobrien            if (!ParseOptions (args, result))
51690075Sobrien                return false;
51790075Sobrien
51890075Sobrien            Error error (m_option_group.NotifyOptionParsingFinished());
519132718Skan            if (error.Fail())
520132718Skan            {
521132718Skan                result.AppendError (error.AsCString());
522132718Skan                result.SetStatus (eReturnStatusFailed);
523132718Skan                return false;
524132718Skan            }
525132718Skan        }
526132718Skan    }
527132718Skan
528132718Skan    if (expr == NULL)
529132718Skan        expr = command;
530132718Skan
531132718Skan    if (EvaluateExpression (expr, &(result.GetOutputStream()), &(result.GetErrorStream()), &result))
532132718Skan        return true;
533132718Skan
534132718Skan    result.SetStatus (eReturnStatusFailed);
535132718Skan    return false;
536132718Skan}
537132718Skan
538132718Skan