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