1 //===-- CommandObjectBreakpointCommand.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 // C Includes
11 // C++ Includes
12 
13 
14 #include "CommandObjectBreakpointCommand.h"
15 #include "CommandObjectBreakpoint.h"
16 
17 #include "lldb/Interpreter/CommandInterpreter.h"
18 #include "lldb/Interpreter/CommandReturnObject.h"
19 #include "lldb/Target/Target.h"
20 #include "lldb/Target/Thread.h"
21 #include "lldb/Breakpoint/BreakpointIDList.h"
22 #include "lldb/Breakpoint/Breakpoint.h"
23 #include "lldb/Breakpoint/BreakpointLocation.h"
24 #include "lldb/Breakpoint/StoppointCallbackContext.h"
25 #include "lldb/Core/State.h"
26 
27 using namespace lldb;
28 using namespace lldb_private;
29 
30 //-------------------------------------------------------------------------
31 // CommandObjectBreakpointCommandAdd::CommandOptions
32 //-------------------------------------------------------------------------
33 
34 CommandObjectBreakpointCommandAdd::CommandOptions::CommandOptions () :
35     Options ()
36 {
37 }
38 
39 CommandObjectBreakpointCommandAdd::CommandOptions::~CommandOptions ()
40 {
41 }
42 
43 lldb::OptionDefinition
44 CommandObjectBreakpointCommandAdd::CommandOptions::g_option_table[] =
45 {
46     { LLDB_OPT_SET_1, true, "script",    's', no_argument, NULL, 0, NULL,
47         "Write the breakpoint command script in the default scripting language."},
48 
49     { LLDB_OPT_SET_2, true, "python",    'p', no_argument, NULL, 0, NULL,
50         "Write the breakpoint command script in the Python scripting language."},
51 
52     { LLDB_OPT_SET_3, true, "commands",  'c', no_argument, NULL, 0, NULL,
53         "Write the breakpoint command script using the command line commands."},
54 
55     { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL }
56 };
57 
58 const lldb::OptionDefinition*
59 CommandObjectBreakpointCommandAdd::CommandOptions::GetDefinitions ()
60 {
61     return g_option_table;
62 }
63 
64 
65 Error
66 CommandObjectBreakpointCommandAdd::CommandOptions::SetOptionValue
67 (
68     int option_idx,
69     const char *option_arg
70 )
71 {
72     Error error;
73     char short_option = (char) m_getopt_table[option_idx].val;
74 
75     switch (short_option)
76       {
77       case 's':
78         m_use_commands = false;
79         m_use_script_language = true;
80         m_script_language = eScriptLanguageDefault;
81         break;
82       case 'p':
83         m_use_commands = false;
84         m_use_script_language = true;
85         m_script_language = eScriptLanguagePython;
86         break;
87       case 'c':
88         m_use_commands = true;
89         m_use_script_language = false;
90         m_script_language = eScriptLanguageNone;
91         break;
92       default:
93         break;
94       }
95     return error;
96 }
97 
98 void
99 CommandObjectBreakpointCommandAdd::CommandOptions::ResetOptionValues ()
100 {
101     Options::ResetOptionValues();
102 
103     m_use_commands = false;
104     m_use_script_language = false;
105     m_script_language = eScriptLanguageNone;
106 }
107 
108 //-------------------------------------------------------------------------
109 // CommandObjectBreakpointCommandAdd
110 //-------------------------------------------------------------------------
111 
112 
113 CommandObjectBreakpointCommandAdd::CommandObjectBreakpointCommandAdd () :
114     CommandObject ("add",
115                    "Adds a set of commands to a breakpoint to be executed whenever a breakpoint is hit.",
116                    "breakpoint command add <cmd-options> <breakpoint-id>")
117 {
118     SetHelpLong (
119 "\nGeneral information about entering breakpoint commands \n\
120 ------------------------------------------------------ \n\
121  \n\
122 This command will cause you to be prompted to enter the command or set \n\
123 of commands you wish to be executed when the specified breakpoint is \n\
124 hit.  You will be told to enter your command(s), and will see a '> ' \n\
125 prompt. Because you can enter one or many commands to be executed when \n\
126 a breakpoint is hit, you will continue to be prompted after each \n\
127 new-line that you enter, until you enter the word 'DONE', which will \n\
128 cause the commands you have entered to be stored with the breakpoint \n\
129 and executed when the breakpoint is hit. \n\
130  \n\
131 Syntax checking is not necessarily done when breakpoint commands are \n\
132 entered.  An improperly written breakpoint command will attempt to get \n\
133 executed when the breakpoint gets hit, and usually silently fail.  If \n\
134 your breakpoint command does not appear to be getting executed, go \n\
135 back and check your syntax. \n\
136  \n\
137  \n\
138 Special information about PYTHON breakpoint commands \n\
139 ---------------------------------------------------- \n\
140  \n\
141 You may enter either one line of Python or multiple lines of Python \n\
142 (including defining whole functions, if desired).  If you enter a \n\
143 single line of Python, that will be passed to the Python interpreter \n\
144 'as is' when the breakpoint gets hit.  If you enter function \n\
145 definitions, they will be passed to the Python interpreter as soon as \n\
146 you finish entering the breakpoint command, and they can be called \n\
147 later (don't forget to add calls to them, if you want them called when \n\
148 the breakpoint is hit).  If you enter multiple lines of Python that \n\
149 are not function definitions, they will be collected into a new, \n\
150 automatically generated Python function, and a call to the newly \n\
151 generated function will be attached to the breakpoint.  Important \n\
152 Note: Because loose Python code gets collected into functions, if you \n\
153 want to access global variables in the 'loose' code, you need to \n\
154 specify that they are global, using the 'global' keyword.  Be sure to \n\
155 use correct Python syntax, including indentation, when entering Python \n\
156 breakpoint commands. \n\
157  \n\
158 Example Python one-line breakpoint command: \n\
159  \n\
160 (lldb) breakpoint command add -p 1 \n\
161 Enter your Python command(s). Type 'DONE' to end. \n\
162 > print \"Hit this breakpoint!\" \n\
163 > DONE \n\
164  \n\
165 Example multiple line Python breakpoint command, using function definition: \n\
166  \n\
167 (lldb) breakpoint command add -p 1 \n\
168 Enter your Python command(s). Type 'DONE' to end. \n\
169 > def breakpoint_output (bp_no): \n\
170 >     out_string = \"Hit breakpoint number \" + repr (bp_no) \n\
171 >     print out_string \n\
172 >     return True \n\
173 > breakpoint_output (1) \n\
174 > DONE \n\
175  \n\
176  \n\
177 Example multiple line Python breakpoint command, using 'loose' Python: \n\
178  \n\
179 (lldb) breakpoint command add -p 1 \n\
180 Enter your Python command(s). Type 'DONE' to end. \n\
181 > global bp_count \n\
182 > bp_count = bp_count + 1 \n\
183 > print \"Hit this breakpoint \" + repr(bp_count) + \" times!\" \n\
184 > DONE \n\
185  \n\
186 In this case, since there is a reference to a global variable, \n\
187 'bp_count', you will also need to make sure 'bp_count' exists and is \n\
188 initialized: \n\
189  \n\
190 (lldb) script \n\
191 >>> bp_count = 0 \n\
192 >>> quit() \n\
193  \n\
194 (lldb)  \n\
195  \n\
196 Special information  debugger command breakpoint commands \n\
197 --------------------------------------------------------- \n\
198  \n\
199 You may enter any debugger command, exactly as you would at the \n\
200 debugger prompt.  You may enter as many debugger commands as you like, \n\
201 but do NOT enter more than one command per line. \n" );
202 }
203 
204 CommandObjectBreakpointCommandAdd::~CommandObjectBreakpointCommandAdd ()
205 {
206 }
207 
208 bool
209 CommandObjectBreakpointCommandAdd::Execute
210 (
211     CommandInterpreter &interpreter,
212     Args& command,
213     CommandReturnObject &result
214 )
215 {
216     Target *target = interpreter.GetDebugger().GetCurrentTarget().get();
217 
218     if (target == NULL)
219     {
220         result.AppendError ("There is not a current executable; there are no breakpoints to which to add commands");
221         result.SetStatus (eReturnStatusFailed);
222         return false;
223     }
224 
225     const BreakpointList &breakpoints = target->GetBreakpointList();
226     size_t num_breakpoints = breakpoints.GetSize();
227 
228     if (num_breakpoints == 0)
229     {
230         result.AppendError ("No breakpoints exist to have commands added");
231         result.SetStatus (eReturnStatusFailed);
232         return false;
233     }
234 
235     if (command.GetArgumentCount() == 0)
236     {
237         result.AppendError ("No breakpoint specified to which to add the commands");
238         result.SetStatus (eReturnStatusFailed);
239         return false;
240     }
241 
242     BreakpointIDList valid_bp_ids;
243     CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids);
244 
245     if (result.Succeeded())
246     {
247         const size_t count = valid_bp_ids.GetSize();
248         for (size_t i = 0; i < count; ++i)
249         {
250             BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i);
251             if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID)
252             {
253                 Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get();
254                 if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID)
255                 {
256                     BreakpointLocationSP bp_loc_sp(bp->FindLocationByID (cur_bp_id.GetLocationID()));
257                     if (bp_loc_sp)
258                     {
259                         if (m_options.m_use_script_language)
260                         {
261                             interpreter.GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (bp_loc_sp->GetLocationOptions(),
262                                                                                                           result);
263                         }
264                         else
265                         {
266                             CollectDataForBreakpointCommandCallback (interpreter, bp_loc_sp->GetLocationOptions(), result);
267                         }
268                     }
269                 }
270                 else
271                 {
272                     if (m_options.m_use_script_language)
273                     {
274                         interpreter.GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (bp->GetOptions(),
275                                                                                                       result);
276                     }
277                     else
278                     {
279                         CollectDataForBreakpointCommandCallback (interpreter, bp->GetOptions(), result);
280                     }
281                 }
282             }
283         }
284     }
285 
286     return result.Succeeded();
287 }
288 
289 Options *
290 CommandObjectBreakpointCommandAdd::GetOptions ()
291 {
292     return &m_options;
293 }
294 
295 const char *g_reader_instructions = "Enter your debugger command(s).  Type 'DONE' to end.";
296 
297 void
298 CommandObjectBreakpointCommandAdd::CollectDataForBreakpointCommandCallback
299 (
300     CommandInterpreter &interpreter,
301     BreakpointOptions *bp_options,
302     CommandReturnObject &result
303 )
304 {
305     InputReaderSP reader_sp (new InputReader(interpreter.GetDebugger()));
306     std::auto_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData());
307     if (reader_sp && data_ap.get())
308     {
309         BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release()));
310         bp_options->SetCallback (CommandObjectBreakpointCommand::BreakpointOptionsCallbackFunction, baton_sp);
311 
312         Error err (reader_sp->Initialize (CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback,
313                                           bp_options,                   // baton
314                                           eInputReaderGranularityLine,  // token size, to pass to callback function
315                                           "DONE",                       // end token
316                                           "> ",                         // prompt
317                                           true));                       // echo input
318         if (err.Success())
319         {
320             interpreter.GetDebugger().PushInputReader (reader_sp);
321             result.SetStatus (eReturnStatusSuccessFinishNoResult);
322         }
323         else
324         {
325             result.AppendError (err.AsCString());
326             result.SetStatus (eReturnStatusFailed);
327         }
328     }
329     else
330     {
331         result.AppendError("out of memory");
332         result.SetStatus (eReturnStatusFailed);
333     }
334 
335 }
336 
337 size_t
338 CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback
339 (
340     void *baton,
341     InputReader &reader,
342     lldb::InputReaderAction notification,
343     const char *bytes,
344     size_t bytes_len
345 )
346 {
347     FILE *out_fh = reader.GetDebugger().GetOutputFileHandle();
348 
349     switch (notification)
350     {
351     case eInputReaderActivate:
352         if (out_fh)
353         {
354             ::fprintf (out_fh, "%s\n", g_reader_instructions);
355             if (reader.GetPrompt())
356                 ::fprintf (out_fh, "%s", reader.GetPrompt());
357         }
358         break;
359 
360     case eInputReaderDeactivate:
361         break;
362 
363     case eInputReaderReactivate:
364         if (out_fh && reader.GetPrompt())
365             ::fprintf (out_fh, "%s", reader.GetPrompt());
366         break;
367 
368     case eInputReaderGotToken:
369         if (bytes && bytes_len && baton)
370         {
371             BreakpointOptions *bp_options = (BreakpointOptions *) baton;
372             if (bp_options)
373             {
374                 Baton *bp_options_baton = bp_options->GetBaton();
375                 if (bp_options_baton)
376                     ((BreakpointOptions::CommandData *)bp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len);
377             }
378         }
379         if (out_fh && !reader.IsDone() && reader.GetPrompt())
380             ::fprintf (out_fh, "%s", reader.GetPrompt());
381         break;
382 
383     case eInputReaderDone:
384         break;
385     }
386 
387     return bytes_len;
388 }
389 
390 
391 //-------------------------------------------------------------------------
392 // CommandObjectBreakpointCommandRemove
393 //-------------------------------------------------------------------------
394 
395 CommandObjectBreakpointCommandRemove::CommandObjectBreakpointCommandRemove () :
396     CommandObject ("remove",
397                    "Remove the set of commands from a breakpoint.",
398                    "breakpoint command remove <breakpoint-id>")
399 {
400 }
401 
402 CommandObjectBreakpointCommandRemove::~CommandObjectBreakpointCommandRemove ()
403 {
404 }
405 
406 bool
407 CommandObjectBreakpointCommandRemove::Execute
408 (
409     CommandInterpreter &interpreter,
410     Args& command,
411     CommandReturnObject &result
412 )
413 {
414     Target *target = interpreter.GetDebugger().GetCurrentTarget().get();
415 
416     if (target == NULL)
417     {
418         result.AppendError ("There is not a current executable; there are no breakpoints from which to remove commands");
419         result.SetStatus (eReturnStatusFailed);
420         return false;
421     }
422 
423     const BreakpointList &breakpoints = target->GetBreakpointList();
424     size_t num_breakpoints = breakpoints.GetSize();
425 
426     if (num_breakpoints == 0)
427     {
428         result.AppendError ("No breakpoints exist to have commands removed");
429         result.SetStatus (eReturnStatusFailed);
430         return false;
431     }
432 
433     if (command.GetArgumentCount() == 0)
434     {
435         result.AppendError ("No breakpoint specified from which to remove the commands");
436         result.SetStatus (eReturnStatusFailed);
437         return false;
438     }
439 
440     BreakpointIDList valid_bp_ids;
441     CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids);
442 
443     if (result.Succeeded())
444     {
445         const size_t count = valid_bp_ids.GetSize();
446         for (size_t i = 0; i < count; ++i)
447         {
448             BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i);
449             if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID)
450             {
451                 Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get();
452                 if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID)
453                 {
454                     BreakpointLocationSP bp_loc_sp (bp->FindLocationByID (cur_bp_id.GetLocationID()));
455                     if (bp_loc_sp)
456                         bp_loc_sp->ClearCallback();
457                     else
458                     {
459                         result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
460                                                      cur_bp_id.GetBreakpointID(),
461                                                      cur_bp_id.GetLocationID());
462                         result.SetStatus (eReturnStatusFailed);
463                         return false;
464                     }
465                 }
466                 else
467                 {
468                     bp->ClearCallback();
469                 }
470             }
471         }
472     }
473     return result.Succeeded();
474 }
475 
476 
477 //-------------------------------------------------------------------------
478 // CommandObjectBreakpointCommandList
479 //-------------------------------------------------------------------------
480 
481 CommandObjectBreakpointCommandList::CommandObjectBreakpointCommandList () :
482     CommandObject ("List",
483                    "List the script or set of commands to be executed when the breakpoint is hit.",
484                    "breakpoint command list <breakpoint-id>")
485 {
486 }
487 
488 CommandObjectBreakpointCommandList::~CommandObjectBreakpointCommandList ()
489 {
490 }
491 
492 bool
493 CommandObjectBreakpointCommandList::Execute
494 (
495     CommandInterpreter &interpreter,
496     Args& command,
497     CommandReturnObject &result
498 )
499 {
500     Target *target = interpreter.GetDebugger().GetCurrentTarget().get();
501 
502     if (target == NULL)
503     {
504         result.AppendError ("There is not a current executable; there are no breakpoints for which to list commands");
505         result.SetStatus (eReturnStatusFailed);
506         return false;
507     }
508 
509     const BreakpointList &breakpoints = target->GetBreakpointList();
510     size_t num_breakpoints = breakpoints.GetSize();
511 
512     if (num_breakpoints == 0)
513     {
514         result.AppendError ("No breakpoints exist for which to list commands");
515         result.SetStatus (eReturnStatusFailed);
516         return false;
517     }
518 
519     if (command.GetArgumentCount() == 0)
520     {
521         result.AppendError ("No breakpoint specified for which to list the commands");
522         result.SetStatus (eReturnStatusFailed);
523         return false;
524     }
525 
526     BreakpointIDList valid_bp_ids;
527     CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids);
528 
529     if (result.Succeeded())
530     {
531         const size_t count = valid_bp_ids.GetSize();
532         for (size_t i = 0; i < count; ++i)
533         {
534             BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i);
535             if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID)
536             {
537                 Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get();
538 
539                 if (bp)
540                 {
541                     const BreakpointOptions *bp_options = NULL;
542                     if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID)
543                     {
544                         BreakpointLocationSP bp_loc_sp(bp->FindLocationByID (cur_bp_id.GetLocationID()));
545                         if (bp_loc_sp)
546                             bp_options = bp_loc_sp->GetOptionsNoCreate();
547                         else
548                         {
549                             result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
550                                                          cur_bp_id.GetBreakpointID(),
551                                                          cur_bp_id.GetLocationID());
552                             result.SetStatus (eReturnStatusFailed);
553                             return false;
554                         }
555                     }
556                     else
557                     {
558                         bp_options = bp->GetOptions();
559                     }
560 
561                     if (bp_options)
562                     {
563                         StreamString id_str;
564                         BreakpointID::GetCanonicalReference (&id_str, cur_bp_id.GetBreakpointID(), cur_bp_id.GetLocationID());
565                         const Baton *baton = bp_options->GetBaton();
566                         if (baton)
567                         {
568                             result.GetOutputStream().Printf ("Breakpoint %s:\n", id_str.GetData());
569                             result.GetOutputStream().IndentMore ();
570                             baton->GetDescription(&result.GetOutputStream(), eDescriptionLevelFull);
571                             result.GetOutputStream().IndentLess ();
572                         }
573                         else
574                         {
575                             result.AppendMessageWithFormat ("Breakpoint %s does not have an associated command.\n", id_str.GetData());
576                         }
577                     }
578                     result.SetStatus (eReturnStatusSuccessFinishResult);
579                 }
580                 else
581                 {
582                     result.AppendErrorWithFormat("Invalid breakpoint ID: %u.\n", cur_bp_id.GetBreakpointID());
583                     result.SetStatus (eReturnStatusFailed);
584                 }
585 
586             }
587         }
588     }
589 
590     return result.Succeeded();
591 }
592 
593 //-------------------------------------------------------------------------
594 // CommandObjectBreakpointCommand
595 //-------------------------------------------------------------------------
596 
597 CommandObjectBreakpointCommand::CommandObjectBreakpointCommand (CommandInterpreter &interpreter) :
598     CommandObjectMultiword ("command",
599                             "A set of commands for adding, removing and examining bits of code to be executed when the breakpoint is hit (breakpoint 'commmands').",
600                             "command <sub-command> [<sub-command-options>] <breakpoint-id>")
601 {
602     bool status;
603     CommandObjectSP add_command_object (new CommandObjectBreakpointCommandAdd ());
604     CommandObjectSP remove_command_object (new CommandObjectBreakpointCommandRemove ());
605     CommandObjectSP list_command_object (new CommandObjectBreakpointCommandList ());
606 
607     add_command_object->SetCommandName ("breakpoint command add");
608     remove_command_object->SetCommandName ("breakpoint command remove");
609     list_command_object->SetCommandName ("breakpoint command list");
610 
611     status = LoadSubCommand (interpreter, "add",    add_command_object);
612     status = LoadSubCommand (interpreter, "remove", remove_command_object);
613     status = LoadSubCommand (interpreter, "list",   list_command_object);
614 }
615 
616 
617 CommandObjectBreakpointCommand::~CommandObjectBreakpointCommand ()
618 {
619 }
620 
621 bool
622 CommandObjectBreakpointCommand::BreakpointOptionsCallbackFunction
623 (
624     void *baton,
625     StoppointCallbackContext *context,
626     lldb::user_id_t break_id,
627     lldb::user_id_t break_loc_id
628 )
629 {
630     bool ret_value = true;
631     if (baton == NULL)
632         return true;
633 
634 
635     BreakpointOptions::CommandData *data = (BreakpointOptions::CommandData *) baton;
636     StringList &commands = data->user_source;
637 
638     if (commands.GetSize() > 0)
639     {
640         uint32_t num_commands = commands.GetSize();
641         CommandReturnObject result;
642         if (context->exe_ctx.target)
643         {
644 
645             Debugger &debugger = context->exe_ctx.target->GetDebugger();
646             CommandInterpreter &interpreter = debugger.GetCommandInterpreter();
647 
648             FILE *out_fh = debugger.GetOutputFileHandle();
649             FILE *err_fh = debugger.GetErrorFileHandle();
650 
651             uint32_t i;
652             for (i = 0; i < num_commands; ++i)
653             {
654 
655                 // First time through we use the context from the stoppoint, after that we use whatever
656                 // has been set by the previous command.
657 
658                 if (!interpreter.HandleCommand (commands.GetStringAtIndex(i), false, result, &context->exe_ctx))
659                     break;
660 
661                 // FIXME: This isn't really the right way to do this.  We should be able to peek at the public
662                 // to see if there is any new events, but that is racey, since the internal process thread has to run and
663                 // deliver the event to the public queue before a run will show up.  So for now we check
664                 // the internal thread state.
665 
666                 lldb::StateType internal_state = context->exe_ctx.process->GetPrivateState();
667                 if (internal_state != eStateStopped)
668                 {
669                     if (i < num_commands - 1)
670                     {
671                         if (out_fh)
672                             ::fprintf (out_fh, "Short-circuiting command execution because target state changed to %s."
673                                                " last command: \"%s\"\n", StateAsCString(internal_state),
674                                                commands.GetStringAtIndex(i));
675                     }
676                     break;
677                 }
678 
679                 if (out_fh)
680                     ::fprintf (out_fh, "%s", result.GetErrorStream().GetData());
681                 if (err_fh)
682                     ::fprintf (err_fh, "%s", result.GetOutputStream().GetData());
683                 result.Clear();
684                 result.SetStatus (eReturnStatusSuccessFinishNoResult);
685             }
686 
687             if (err_fh && !result.Succeeded() && i < num_commands)
688                 ::fprintf (err_fh, "Attempt to execute '%s' failed.\n", commands.GetStringAtIndex(i));
689 
690             if (out_fh)
691                 ::fprintf (out_fh, "%s", result.GetErrorStream().GetData());
692 
693             if (err_fh)
694                 ::fprintf (err_fh, "%s", result.GetOutputStream().GetData());
695         }
696     }
697     return ret_value;
698 }
699 
700