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 standard debugger 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                    "Add a set of commands to a breakpoint, to be executed whenever the 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 about 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().GetSelectedTarget().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 (interpreter,
262                                                                                                          bp_loc_sp->GetLocationOptions(),
263                                                                                                          result);
264                         }
265                         else
266                         {
267                             CollectDataForBreakpointCommandCallback (interpreter,
268                                                                      bp_loc_sp->GetLocationOptions(),
269                                                                      result);
270                         }
271                     }
272                 }
273                 else
274                 {
275                     if (m_options.m_use_script_language)
276                     {
277                         interpreter.GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (interpreter,
278                                                                                                      bp->GetOptions(),
279                                                                                                      result);
280                     }
281                     else
282                     {
283                         CollectDataForBreakpointCommandCallback (interpreter,
284                                                                  bp->GetOptions(),
285                                                                  result);
286                     }
287                 }
288             }
289         }
290     }
291 
292     return result.Succeeded();
293 }
294 
295 Options *
296 CommandObjectBreakpointCommandAdd::GetOptions ()
297 {
298     return &m_options;
299 }
300 
301 const char *g_reader_instructions = "Enter your debugger command(s).  Type 'DONE' to end.";
302 
303 void
304 CommandObjectBreakpointCommandAdd::CollectDataForBreakpointCommandCallback
305 (
306     CommandInterpreter &interpreter,
307     BreakpointOptions *bp_options,
308     CommandReturnObject &result
309 )
310 {
311     InputReaderSP reader_sp (new InputReader(interpreter.GetDebugger()));
312     std::auto_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData());
313     if (reader_sp && data_ap.get())
314     {
315         BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release()));
316         bp_options->SetCallback (CommandObjectBreakpointCommand::BreakpointOptionsCallbackFunction, baton_sp);
317 
318         Error err (reader_sp->Initialize (CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback,
319                                           bp_options,                   // baton
320                                           eInputReaderGranularityLine,  // token size, to pass to callback function
321                                           "DONE",                       // end token
322                                           "> ",                         // prompt
323                                           true));                       // echo input
324         if (err.Success())
325         {
326             interpreter.GetDebugger().PushInputReader (reader_sp);
327             result.SetStatus (eReturnStatusSuccessFinishNoResult);
328         }
329         else
330         {
331             result.AppendError (err.AsCString());
332             result.SetStatus (eReturnStatusFailed);
333         }
334     }
335     else
336     {
337         result.AppendError("out of memory");
338         result.SetStatus (eReturnStatusFailed);
339     }
340 
341 }
342 
343 size_t
344 CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback
345 (
346     void *baton,
347     InputReader &reader,
348     lldb::InputReaderAction notification,
349     const char *bytes,
350     size_t bytes_len
351 )
352 {
353     FILE *out_fh = reader.GetDebugger().GetOutputFileHandle();
354 
355     switch (notification)
356     {
357     case eInputReaderActivate:
358         if (out_fh)
359         {
360             ::fprintf (out_fh, "%s\n", g_reader_instructions);
361             if (reader.GetPrompt())
362                 ::fprintf (out_fh, "%s", reader.GetPrompt());
363         }
364         break;
365 
366     case eInputReaderDeactivate:
367         break;
368 
369     case eInputReaderReactivate:
370         if (out_fh && reader.GetPrompt())
371             ::fprintf (out_fh, "%s", reader.GetPrompt());
372         break;
373 
374     case eInputReaderGotToken:
375         if (bytes && bytes_len && baton)
376         {
377             BreakpointOptions *bp_options = (BreakpointOptions *) baton;
378             if (bp_options)
379             {
380                 Baton *bp_options_baton = bp_options->GetBaton();
381                 if (bp_options_baton)
382                     ((BreakpointOptions::CommandData *)bp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len);
383             }
384         }
385         if (out_fh && !reader.IsDone() && reader.GetPrompt())
386             ::fprintf (out_fh, "%s", reader.GetPrompt());
387         break;
388 
389     case eInputReaderDone:
390         break;
391     }
392 
393     return bytes_len;
394 }
395 
396 
397 //-------------------------------------------------------------------------
398 // CommandObjectBreakpointCommandRemove
399 //-------------------------------------------------------------------------
400 
401 CommandObjectBreakpointCommandRemove::CommandObjectBreakpointCommandRemove () :
402     CommandObject ("remove",
403                    "Remove the set of commands from a breakpoint.",
404                    "breakpoint command remove <breakpoint-id>")
405 {
406 }
407 
408 CommandObjectBreakpointCommandRemove::~CommandObjectBreakpointCommandRemove ()
409 {
410 }
411 
412 bool
413 CommandObjectBreakpointCommandRemove::Execute
414 (
415     CommandInterpreter &interpreter,
416     Args& command,
417     CommandReturnObject &result
418 )
419 {
420     Target *target = interpreter.GetDebugger().GetSelectedTarget().get();
421 
422     if (target == NULL)
423     {
424         result.AppendError ("There is not a current executable; there are no breakpoints from which to remove commands");
425         result.SetStatus (eReturnStatusFailed);
426         return false;
427     }
428 
429     const BreakpointList &breakpoints = target->GetBreakpointList();
430     size_t num_breakpoints = breakpoints.GetSize();
431 
432     if (num_breakpoints == 0)
433     {
434         result.AppendError ("No breakpoints exist to have commands removed");
435         result.SetStatus (eReturnStatusFailed);
436         return false;
437     }
438 
439     if (command.GetArgumentCount() == 0)
440     {
441         result.AppendError ("No breakpoint specified from which to remove the commands");
442         result.SetStatus (eReturnStatusFailed);
443         return false;
444     }
445 
446     BreakpointIDList valid_bp_ids;
447     CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids);
448 
449     if (result.Succeeded())
450     {
451         const size_t count = valid_bp_ids.GetSize();
452         for (size_t i = 0; i < count; ++i)
453         {
454             BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i);
455             if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID)
456             {
457                 Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get();
458                 if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID)
459                 {
460                     BreakpointLocationSP bp_loc_sp (bp->FindLocationByID (cur_bp_id.GetLocationID()));
461                     if (bp_loc_sp)
462                         bp_loc_sp->ClearCallback();
463                     else
464                     {
465                         result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
466                                                      cur_bp_id.GetBreakpointID(),
467                                                      cur_bp_id.GetLocationID());
468                         result.SetStatus (eReturnStatusFailed);
469                         return false;
470                     }
471                 }
472                 else
473                 {
474                     bp->ClearCallback();
475                 }
476             }
477         }
478     }
479     return result.Succeeded();
480 }
481 
482 
483 //-------------------------------------------------------------------------
484 // CommandObjectBreakpointCommandList
485 //-------------------------------------------------------------------------
486 
487 CommandObjectBreakpointCommandList::CommandObjectBreakpointCommandList () :
488     CommandObject ("List",
489                    "List the script or set of commands to be executed when the breakpoint is hit.",
490                    "breakpoint command list <breakpoint-id>")
491 {
492 }
493 
494 CommandObjectBreakpointCommandList::~CommandObjectBreakpointCommandList ()
495 {
496 }
497 
498 bool
499 CommandObjectBreakpointCommandList::Execute
500 (
501     CommandInterpreter &interpreter,
502     Args& command,
503     CommandReturnObject &result
504 )
505 {
506     Target *target = interpreter.GetDebugger().GetSelectedTarget().get();
507 
508     if (target == NULL)
509     {
510         result.AppendError ("There is not a current executable; there are no breakpoints for which to list commands");
511         result.SetStatus (eReturnStatusFailed);
512         return false;
513     }
514 
515     const BreakpointList &breakpoints = target->GetBreakpointList();
516     size_t num_breakpoints = breakpoints.GetSize();
517 
518     if (num_breakpoints == 0)
519     {
520         result.AppendError ("No breakpoints exist for which to list commands");
521         result.SetStatus (eReturnStatusFailed);
522         return false;
523     }
524 
525     if (command.GetArgumentCount() == 0)
526     {
527         result.AppendError ("No breakpoint specified for which to list the commands");
528         result.SetStatus (eReturnStatusFailed);
529         return false;
530     }
531 
532     BreakpointIDList valid_bp_ids;
533     CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids);
534 
535     if (result.Succeeded())
536     {
537         const size_t count = valid_bp_ids.GetSize();
538         for (size_t i = 0; i < count; ++i)
539         {
540             BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i);
541             if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID)
542             {
543                 Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get();
544 
545                 if (bp)
546                 {
547                     const BreakpointOptions *bp_options = NULL;
548                     if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID)
549                     {
550                         BreakpointLocationSP bp_loc_sp(bp->FindLocationByID (cur_bp_id.GetLocationID()));
551                         if (bp_loc_sp)
552                             bp_options = bp_loc_sp->GetOptionsNoCreate();
553                         else
554                         {
555                             result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
556                                                          cur_bp_id.GetBreakpointID(),
557                                                          cur_bp_id.GetLocationID());
558                             result.SetStatus (eReturnStatusFailed);
559                             return false;
560                         }
561                     }
562                     else
563                     {
564                         bp_options = bp->GetOptions();
565                     }
566 
567                     if (bp_options)
568                     {
569                         StreamString id_str;
570                         BreakpointID::GetCanonicalReference (&id_str, cur_bp_id.GetBreakpointID(), cur_bp_id.GetLocationID());
571                         const Baton *baton = bp_options->GetBaton();
572                         if (baton)
573                         {
574                             result.GetOutputStream().Printf ("Breakpoint %s:\n", id_str.GetData());
575                             result.GetOutputStream().IndentMore ();
576                             baton->GetDescription(&result.GetOutputStream(), eDescriptionLevelFull);
577                             result.GetOutputStream().IndentLess ();
578                         }
579                         else
580                         {
581                             result.AppendMessageWithFormat ("Breakpoint %s does not have an associated command.\n", id_str.GetData());
582                         }
583                     }
584                     result.SetStatus (eReturnStatusSuccessFinishResult);
585                 }
586                 else
587                 {
588                     result.AppendErrorWithFormat("Invalid breakpoint ID: %u.\n", cur_bp_id.GetBreakpointID());
589                     result.SetStatus (eReturnStatusFailed);
590                 }
591 
592             }
593         }
594     }
595 
596     return result.Succeeded();
597 }
598 
599 //-------------------------------------------------------------------------
600 // CommandObjectBreakpointCommand
601 //-------------------------------------------------------------------------
602 
603 CommandObjectBreakpointCommand::CommandObjectBreakpointCommand (CommandInterpreter &interpreter) :
604     CommandObjectMultiword ("command",
605                             "A set of commands for adding, removing and examining bits of code to be executed when the breakpoint is hit (breakpoint 'commmands').",
606                             "command <sub-command> [<sub-command-options>] <breakpoint-id>")
607 {
608     bool status;
609     CommandObjectSP add_command_object (new CommandObjectBreakpointCommandAdd ());
610     CommandObjectSP remove_command_object (new CommandObjectBreakpointCommandRemove ());
611     CommandObjectSP list_command_object (new CommandObjectBreakpointCommandList ());
612 
613     add_command_object->SetCommandName ("breakpoint command add");
614     remove_command_object->SetCommandName ("breakpoint command remove");
615     list_command_object->SetCommandName ("breakpoint command list");
616 
617     status = LoadSubCommand (interpreter, "add",    add_command_object);
618     status = LoadSubCommand (interpreter, "remove", remove_command_object);
619     status = LoadSubCommand (interpreter, "list",   list_command_object);
620 }
621 
622 
623 CommandObjectBreakpointCommand::~CommandObjectBreakpointCommand ()
624 {
625 }
626 
627 bool
628 CommandObjectBreakpointCommand::BreakpointOptionsCallbackFunction
629 (
630     void *baton,
631     StoppointCallbackContext *context,
632     lldb::user_id_t break_id,
633     lldb::user_id_t break_loc_id
634 )
635 {
636     bool ret_value = true;
637     if (baton == NULL)
638         return true;
639 
640 
641     BreakpointOptions::CommandData *data = (BreakpointOptions::CommandData *) baton;
642     StringList &commands = data->user_source;
643 
644     if (commands.GetSize() > 0)
645     {
646         uint32_t num_commands = commands.GetSize();
647         CommandReturnObject result;
648         if (context->exe_ctx.target)
649         {
650 
651             Debugger &debugger = context->exe_ctx.target->GetDebugger();
652             CommandInterpreter &interpreter = debugger.GetCommandInterpreter();
653 
654             FILE *out_fh = debugger.GetOutputFileHandle();
655             FILE *err_fh = debugger.GetErrorFileHandle();
656 
657             uint32_t i;
658             for (i = 0; i < num_commands; ++i)
659             {
660 
661                 // First time through we use the context from the stoppoint, after that we use whatever
662                 // has been set by the previous command.
663 
664                 if (!interpreter.HandleCommand (commands.GetStringAtIndex(i), false, result, &context->exe_ctx))
665                     break;
666 
667                 // FIXME: This isn't really the right way to do this.  We should be able to peek at the public
668                 // to see if there is any new events, but that is racey, since the internal process thread has to run and
669                 // deliver the event to the public queue before a run will show up.  So for now we check
670                 // the internal thread state.
671 
672                 lldb::StateType internal_state = context->exe_ctx.process->GetPrivateState();
673                 if (internal_state != eStateStopped)
674                 {
675                     if (i < num_commands - 1)
676                     {
677                         if (out_fh)
678                             ::fprintf (out_fh, "Short-circuiting command execution because target state changed to %s."
679                                                " last command: \"%s\"\n", StateAsCString(internal_state),
680                                                commands.GetStringAtIndex(i));
681                     }
682                     break;
683                 }
684 
685                 if (out_fh)
686                     ::fprintf (out_fh, "%s", result.GetErrorStream().GetData());
687                 if (err_fh)
688                     ::fprintf (err_fh, "%s", result.GetOutputStream().GetData());
689                 result.Clear();
690                 result.SetStatus (eReturnStatusSuccessFinishNoResult);
691             }
692 
693             if (err_fh && !result.Succeeded() && i < num_commands)
694                 ::fprintf (err_fh, "Attempt to execute '%s' failed.\n", commands.GetStringAtIndex(i));
695 
696             if (out_fh)
697                 ::fprintf (out_fh, "%s", result.GetErrorStream().GetData());
698 
699             if (err_fh)
700                 ::fprintf (err_fh, "%s", result.GetOutputStream().GetData());
701         }
702     }
703     return ret_value;
704 }
705 
706