Initial checkin of lldb code from internal Apple repo.


git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@105619 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/tools/driver/Driver.cpp b/tools/driver/Driver.cpp
new file mode 100644
index 0000000..8cca697
--- /dev/null
+++ b/tools/driver/Driver.cpp
@@ -0,0 +1,1265 @@
+//===-- Driver.cpp ----------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Driver.h"
+
+#include <getopt.h>
+#include <libgen.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "IOChannel.h"
+#include <LLDB/SBCommandInterpreter.h>
+#include <LLDB/SBCommandReturnObject.h>
+#include <LLDB/SBCommunication.h>
+#include <LLDB/SBDebugger.h>
+#include <LLDB/SBEvent.h>
+#include <LLDB/SBHostOS.h>
+#include <LLDB/SBListener.h>
+#include <LLDB/SBSourceManager.h>
+#include <LLDB/SBTarget.h>
+#include <LLDB/SBThread.h>
+#include <LLDB/SBProcess.h>
+
+using namespace lldb;
+
+static void reset_stdin_termios ();
+static struct termios g_old_stdin_termios;
+
+// In the Driver::MainLoop, we change the terminal settings.  This function is
+// added as an atexit handler to make sure we clean them up.
+static void
+reset_stdin_termios ()
+{
+    ::tcsetattr (STDIN_FILENO, TCSANOW, &g_old_stdin_termios);
+}
+
+static lldb::OptionDefinition g_options[] =
+{
+    { 0,  true,  "help",            'h',  no_argument,        NULL,  NULL,  NULL,
+        "Prints out the usage information for the LLDB debugger." },
+
+    { 1,  true,  "version",         'v',  no_argument,        NULL,  NULL,  NULL,
+        "Prints out the current version number of the LLDB debugger." },
+
+    { 2,  false,  "file",           'f',  required_argument,  NULL,  NULL,  "<filename>",
+        "Tells the debugger to use the file <filename> as the program to be debugged." },
+
+    { 2,  false,  "arch",           'a',  required_argument,  NULL,  NULL,  "<architecture>",
+        "Tells the debugger to use the specified architecture when starting and running the program.  <architecture> must be one of the architectures for which the program was compiled." },
+
+    { 2,  false,  "script-language",'l',  required_argument,  NULL,  NULL,  "<scripting-language>",
+        "Tells the debugger to use the specified scripting language for user-defined scripts, rather than the default.  Valid scripting languages that can be specified include Python, Perl, Ruby and Tcl.  Currently only the Python extensions have been implemented." },
+
+    { 2,  false,  "debug",          'd',  no_argument,        NULL,  NULL,  NULL,
+        "Tells the debugger to print out extra information for debugging itself." },
+
+    { 2,  false,  "source",         's',  required_argument,  NULL,  NULL,  "<file>",
+        "Tells the debugger to read in and execute the file <file>, which should contain lldb commands." },
+
+    { 3,  false,  "crash-log",      'c',  required_argument,  NULL,  NULL,  "<file>",
+        "Load executable images from a crash log for symbolication." },
+
+    { 0, false, NULL, 0, 0, NULL, NULL,  NULL, NULL }
+};
+
+
+Driver::Driver () :
+    SBBroadcaster ("Driver"),
+    m_editline_pty (),
+    m_editline_slave_fh (NULL),
+    m_editline_reader (),
+    m_io_channel_ap (),
+    m_option_data (),
+    m_waiting_for_command (false)
+{
+}
+
+Driver::~Driver ()
+{
+}
+
+void
+Driver::CloseIOChannelFile ()
+{
+    // Write and End of File sequence to the file descriptor to ensure any
+    // read functions can exit.
+    char eof_str[] = "\x04";
+    ::write (m_editline_pty.GetMasterFileDescriptor(), eof_str, strlen(eof_str));
+
+    m_editline_pty.CloseMasterFileDescriptor();
+
+    if (m_editline_slave_fh)
+    {
+        ::fclose (m_editline_slave_fh);
+        m_editline_slave_fh = NULL;
+    }
+}
+
+// This function takes INDENT, which tells how many spaces to output at the front of each line; SPACES, which is
+// a string that is output_max_columns long, containing spaces; and TEXT, which is the text that is to be output.
+// It outputs the text, on multiple lines if necessary, to RESULT, with INDENT spaces at the front of each line.  It
+// breaks lines on spaces, tabs or newlines, shortening the line if necessary to not break in the middle of a word.
+// It assumes that each output line should contain a maximum of OUTPUT_MAX_COLUMNS characters.
+
+void
+OutputFormattedUsageText (FILE *out, int indent, char *spaces, const char *text, int output_max_columns)
+{
+    int len = strlen (text);
+    std::string text_string (text);
+    std::string spaces_string (spaces);
+
+    // Force indentation to be reasonable.
+    if (indent >= output_max_columns)
+        indent = 0;
+
+    // Will it all fit on one line?
+
+    if (len + indent < output_max_columns)
+        // Output as a single line
+        fprintf (out, "%s%s\n", spaces_string.substr (0, indent).c_str(), text);
+    else
+    {
+        // We need to break it up into multiple lines.
+        int text_width = output_max_columns - indent - 1;
+        int start = 0;
+        int end = start;
+        int final_end = len;
+        int sub_len;
+
+        while (end < final_end)
+        {
+              // Dont start the 'text' on a space, since we're already outputting the indentation.
+              while ((start < final_end) && (text[start] == ' '))
+                  start++;
+
+              end = start + text_width;
+              if (end > final_end)
+                  end = final_end;
+              else
+              {
+                  // If we're not at the end of the text, make sure we break the line on white space.
+                  while (end > start
+                         && text[end] != ' ' && text[end] != '\t' && text[end] != '\n')
+                      end--;
+              }
+              sub_len = end - start;
+              std::string substring = text_string.substr (start, sub_len);
+              fprintf (out, "%s%s\n", spaces_string.substr(0, indent).c_str(), substring.c_str());
+              start = end + 1;
+        }
+    }
+}
+
+void
+ShowUsage (FILE *out, lldb::OptionDefinition *option_table, Driver::OptionData data)
+{
+    uint32_t screen_width = 80;
+    uint32_t indent_level = 0;
+    const char *name = "lldb";
+    char spaces[screen_width+1];
+    uint32_t i;
+
+    for (i = 0; i < screen_width; ++i)
+      spaces[i] = ' ';
+    spaces[i] = '\n';
+
+    std::string spaces_string (spaces);
+
+    fprintf (out, "\nUsage:\n\n");
+
+    indent_level += 2;
+
+
+    // First, show each usage level set of options, e.g. <cmd> [options-for-level-0]
+    //                                                   <cmd> [options-for-level-1]
+    //                                                   etc.
+
+    uint32_t usage_level = 0;
+    uint32_t num_options;
+
+    for (num_options = 0; option_table[num_options].long_option != NULL; ++num_options);
+
+    for (i = 0; i < num_options; ++i)
+    {
+        if (i == 0 || option_table[i].usage_level > usage_level)
+        {
+            // Start a new level.
+            usage_level = option_table[i].usage_level;
+            if (usage_level > 0)
+                fprintf (out, "\n\n");
+            fprintf (out, "%s%s", spaces_string.substr(0, indent_level).c_str(), name);
+        }
+
+        if (option_table[i].required)
+        {
+            if (option_table[i].option_has_arg == required_argument)
+                fprintf (out, " -%c %s", option_table[i].short_option, option_table[i].argument_name);
+            else if (option_table[i].option_has_arg == optional_argument)
+                fprintf (out, " -%c [%s]", option_table[i].short_option, option_table[i].argument_name);
+            else
+                fprintf (out, " -%c", option_table[i].short_option);
+        }
+        else
+        {
+            if (option_table[i].option_has_arg == required_argument)
+                fprintf (out, " [-%c %s]", option_table[i].short_option, option_table[i].argument_name);
+            else if (option_table[i].option_has_arg == optional_argument)
+                fprintf (out, " [-%c [%s]]", option_table[i].short_option, option_table[i].argument_name);
+            else
+                fprintf (out, " [-%c]", option_table[i].short_option);
+        }
+    }
+
+    fprintf (out, "\n\n");
+
+    // Now print out all the detailed information about the various options:  long form, short form and help text:
+    //   -- long_name <argument>
+    //   - short <argument>
+    //   help text
+
+    // This variable is used to keep track of which options' info we've printed out, because some options can be in
+    // more than one usage level, but we only want to print the long form of its information once.
+
+    Driver::OptionData::OptionSet options_seen;
+    Driver::OptionData::OptionSet::iterator pos;
+
+    indent_level += 5;
+
+    for (i = 0; i < num_options; ++i)
+    {
+        // Only print this option if we haven't already seen it.
+        pos = options_seen.find (option_table[i].short_option);
+        if (pos == options_seen.end())
+        {
+            options_seen.insert (option_table[i].short_option);
+            fprintf (out, "%s-%c ", spaces_string.substr(0, indent_level).c_str(), option_table[i].short_option);
+            if (option_table[i].argument_name != NULL)
+                fprintf (out, "%s", option_table[i].argument_name);
+            fprintf (out, "\n");
+            fprintf (out, "%s--%s ", spaces_string.substr(0, indent_level).c_str(), option_table[i].long_option);
+            if (option_table[i].argument_name != NULL)
+                fprintf (out, "%s", option_table[i].argument_name);
+            fprintf (out, "\n");
+            indent_level += 5;
+            OutputFormattedUsageText (out, indent_level, spaces, option_table[i].usage_text, screen_width);
+            indent_level -= 5;
+            fprintf (out, "\n");
+        }
+    }
+
+    indent_level -= 5;
+
+    fprintf (out, "\n%s('%s <filename>' also works, to specify the file to be debugged.)\n\n",
+             spaces_string.substr(0, indent_level).c_str(), name);
+}
+
+void
+BuildGetOptTable (lldb::OptionDefinition *expanded_option_table, struct option **getopt_table, int num_options)
+{
+    if (num_options == 0)
+        return;
+
+    uint32_t i;
+    uint32_t j;
+    std::bitset<256> option_seen;
+
+    for (i = 0, j = 0; i < num_options; ++i)
+      {
+        char short_opt = expanded_option_table[i].short_option;
+
+        if (option_seen.test(short_opt) == false)
+          {
+            (*getopt_table)[j].name    = expanded_option_table[i].long_option;
+            (*getopt_table)[j].has_arg = expanded_option_table[i].option_has_arg;
+            (*getopt_table)[j].flag    = NULL;
+            (*getopt_table)[j].val     = expanded_option_table[i].short_option;
+            option_seen.set(short_opt);
+            ++j;
+          }
+      }
+
+    (*getopt_table)[j].name    = NULL;
+    (*getopt_table)[j].has_arg = 0;
+    (*getopt_table)[j].flag    = NULL;
+    (*getopt_table)[j].val     = 0;
+
+}
+
+SBError
+ParseOptions (Driver::OptionData &data, int argc, const char **argv)
+{
+    SBError error;
+    std::string option_string;
+    struct option *long_options = NULL;
+    int num_options;
+
+    for (num_options = 0; g_options[num_options].long_option != NULL; ++num_options);
+
+    if (num_options == 0)
+    {
+        if (argc > 1)
+            error.SetErrorStringWithFormat ("invalid number of options");
+        return error;
+    }
+
+    long_options = (struct option *) malloc ((num_options + 1) * sizeof (struct option));
+
+    BuildGetOptTable (g_options, &long_options, num_options);
+
+    if (long_options == NULL)
+    {
+        error.SetErrorStringWithFormat ("invalid long options");
+        return error;
+    }
+
+    // Build the option_string argument for call to getopt_long.
+
+    for (int i = 0; long_options[i].name != NULL; ++i)
+    {
+        if (long_options[i].flag == NULL)
+        {
+            option_string.push_back ((char) long_options[i].val);
+            switch (long_options[i].has_arg)
+            {
+                default:
+                case no_argument:
+                    break;
+                case required_argument:
+                    option_string.push_back (':');
+                    break;
+                case optional_argument:
+                    option_string.append ("::");
+                    break;
+            }
+        }
+    }
+
+    // Prepare for & make calls to getopt_long.
+
+    optreset = 1;
+    optind = 1;
+    int val;
+    while (1)
+    {
+        int long_options_index = -1;
+        val = ::getopt_long (argc, (char * const *) argv, option_string.c_str(), long_options, &long_options_index);
+
+        if (val == -1)
+            break;
+        else if (val == '?')
+        {
+            data.m_print_help = true;
+            error.SetErrorStringWithFormat ("unknown or ambiguous option");
+            break;
+        }
+        else if (val == 0)
+            continue;
+        else
+        {
+            data.m_seen_options.insert ((char) val);
+            if (long_options_index == -1)
+            {
+                for (int i = 0;
+                     long_options[i].name || long_options[i].has_arg || long_options[i].flag || long_options[i].val;
+                     ++i)
+                {
+                    if (long_options[i].val == val)
+                    {
+                        long_options_index = i;
+                        break;
+                    }
+                }
+            }
+
+            if (long_options_index >= 0)
+            {
+                error = Driver::SetOptionValue (long_options_index,
+                                                long_options[long_options_index].has_arg == no_argument ? NULL : optarg,
+                                                data);
+            }
+            else
+            {
+                error.SetErrorStringWithFormat ("invalid option with value %i", val);
+            }
+            if (error.Fail())
+                break;
+        }
+    }
+
+    return error;
+}
+
+Driver::OptionData::OptionData () :
+    m_filename(),
+    m_script_lang (lldb::eScriptLanguageDefault),
+    m_source_command_files (),
+    m_debug_mode (false),
+    m_print_help (false),
+    m_print_version (false)
+
+{
+}
+
+Driver::OptionData::~OptionData ()
+{
+}
+
+void
+Driver::OptionData::Clear ()
+{
+    m_filename.clear ();
+    m_script_lang = lldb::eScriptLanguageDefault;
+    m_source_command_files.clear ();
+    m_debug_mode = false;
+    m_print_help = false;
+    m_print_version = false;
+}
+
+SBError
+Driver::SetOptionValue (int option_idx, const char *option_arg, Driver::OptionData &option_data)
+{
+    SBError error;
+    const char short_option = (char) g_options[option_idx].short_option;
+
+    switch (short_option)
+    {
+        case 'h':
+            option_data.m_print_help = true;
+            break;
+
+        case 'v':
+            option_data.m_print_version = true;
+            break;
+
+        case 'c':
+            option_data.m_crash_log = option_arg;
+            break;
+
+        case 'f':
+            {
+                SBFileSpec file(option_arg);
+                if (file.Exists())
+                    option_data.m_filename = option_arg;
+                else
+                    error.SetErrorStringWithFormat("file specified in --file (-f) option doesn't exist: '%s'", option_arg);
+            }
+            break;
+
+        case 'a':
+            if (!SBDebugger::SetDefaultArchitecture (option_arg))
+                error.SetErrorStringWithFormat("invalid architecture in the -a or --arch option: '%s'", option_arg);
+            break;
+
+        case 'l':
+            option_data.m_script_lang = SBDebugger::GetScriptingLanguage (option_arg);
+            break;
+
+        case 'd':
+            option_data.m_debug_mode = true;
+            break;
+
+        case 's':
+            {
+                SBFileSpec file(option_arg);
+                if (file.Exists())
+                    option_data.m_source_command_files.push_back (option_arg);
+                else
+                    error.SetErrorStringWithFormat("file specified in --source (-s) option doesn't exist: '%s'", option_arg);
+            }
+            break;
+
+        default:
+            option_data.m_print_help = true;
+            error.SetErrorStringWithFormat ("unrecognized option %c", short_option);
+            break;
+    }
+
+    return error;
+}
+
+void
+Driver::ResetOptionValues ()
+{
+    m_option_data.Clear ();
+}
+
+const char *
+Driver::GetFilename() const
+{
+    if (m_option_data.m_filename.empty())
+        return NULL;
+    return m_option_data.m_filename.c_str();
+}
+
+const char *
+Driver::GetCrashLogFilename() const
+{
+    if (m_option_data.m_crash_log.empty())
+        return NULL;
+    return m_option_data.m_crash_log.c_str();
+}
+
+lldb::ScriptLanguage
+Driver::GetScriptLanguage() const
+{
+    return m_option_data.m_script_lang;
+}
+
+size_t
+Driver::GetNumSourceCommandFiles () const
+{
+    return m_option_data.m_source_command_files.size();
+}
+
+const char *
+Driver::GetSourceCommandFileAtIndex (uint32_t idx) const
+{
+    if (idx < m_option_data.m_source_command_files.size())
+        return m_option_data.m_source_command_files[idx].c_str();
+    return NULL;
+}
+
+bool
+Driver::GetDebugMode() const
+{
+    return m_option_data.m_debug_mode;
+}
+
+
+// Check the arguments that were passed to this program to make sure they are valid and to get their
+// argument values (if any).  Return a boolean value indicating whether or not to start up the full
+// debugger (i.e. the Command Interpreter) or not.  Return FALSE if the arguments were invalid OR
+// if the user only wanted help or version information.
+
+bool
+Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, FILE *err_fh)
+{
+    bool valid = true;
+
+    ResetOptionValues ();
+
+    if (argc == 2 && *(argv[1]) != '-')
+    {
+        m_option_data.m_filename = argv[1];
+    }
+    else
+    {
+        SBCommandReturnObject result;
+
+        SBError error = ParseOptions (m_option_data, argc, argv);
+        if (error.Fail())
+        {
+            const char *error_cstr = error.GetCString ();
+            if (error_cstr)
+                ::fprintf (err_fh, "error: %s\n", error_cstr);
+        }
+    }
+
+    // Check to see if they just invoked the debugger with a filename.
+
+
+    if (m_option_data.m_print_help)
+    {
+        ShowUsage (out_fh, g_options, m_option_data);
+        valid = false;
+    }
+    else if (m_option_data.m_print_version)
+    {
+        ::fprintf (out_fh, "%s\n", SBDebugger::GetVersionString());
+        valid = false;
+    }
+    else if (! m_option_data.m_crash_log.empty())
+    {
+        // Handle crash log stuff here.
+    }
+    else
+    {
+        // All other combinations are valid; do nothing more here.
+    }
+
+    return valid;
+}
+
+void
+Driver::GetProcessSTDOUT ()
+{
+    //  The process has stuff waiting for stdout; get it and write it out to the appropriate place.
+    char stdio_buffer[1024];
+    size_t len;
+    while ((len = SBDebugger::GetCurrentTarget().GetProcess().GetSTDOUT (stdio_buffer, sizeof (stdio_buffer))) > 0)
+        m_io_channel_ap->OutWrite (stdio_buffer, len);
+}
+
+void
+Driver::GetProcessSTDERR ()
+{
+    //  The process has stuff waiting for stderr; get it and write it out to the appropriate place.
+    char stdio_buffer[1024];
+    size_t len;
+    while ((len = SBDebugger::GetCurrentTarget().GetProcess().GetSTDERR (stdio_buffer, sizeof (stdio_buffer))) > 0)
+        m_io_channel_ap->ErrWrite (stdio_buffer, len);
+}
+
+void
+Driver::UpdateCurrentThread ()
+{
+    using namespace lldb;
+    SBProcess process(SBDebugger::GetCurrentTarget().GetProcess());
+    if (process.IsValid())
+    {
+        SBThread curr_thread (process.GetCurrentThread());
+        SBThread thread;
+        StopReason curr_thread_stop_reason = eStopReasonInvalid;
+        curr_thread_stop_reason = curr_thread.GetStopReason();
+
+        if (!curr_thread.IsValid() ||
+            curr_thread_stop_reason == eStopReasonInvalid ||
+            curr_thread_stop_reason == eStopReasonNone)
+        {
+            // Prefer a thread that has just completed its plan over another thread as current thread.
+            SBThread plan_thread;
+            SBThread other_thread;
+            const size_t num_threads = process.GetNumThreads();
+            size_t i;
+            for (i = 0; i < num_threads; ++i)
+            {
+                thread = process.GetThreadAtIndex(i);
+                StopReason thread_stop_reason = thread.GetStopReason();
+                switch (thread_stop_reason)
+                {
+                default:
+                case eStopReasonInvalid:
+                case eStopReasonNone:
+                    break;
+
+                case eStopReasonTrace:
+                case eStopReasonBreakpoint:
+                case eStopReasonWatchpoint:
+                case eStopReasonSignal:
+                case eStopReasonException:
+                    if (!other_thread.IsValid())
+                        other_thread = thread;
+                    break;
+                case eStopReasonPlanComplete:
+                    if (!plan_thread.IsValid())
+                        plan_thread = thread;
+                    break;
+                }
+            }
+            if (plan_thread.IsValid())
+                process.SetCurrentThread (plan_thread);
+            else if (other_thread.IsValid())
+                process.SetCurrentThread (other_thread);
+            else
+            {
+                if (curr_thread.IsValid())
+                    thread = curr_thread;
+                else
+                    thread = process.GetThreadAtIndex(0);
+
+                if (thread.IsValid())
+                    process.SetCurrentThread (thread);
+            }
+        }
+    }
+}
+
+
+// This function handles events that were broadcast by the process.
+void
+Driver::HandleProcessEvent (const SBEvent &event)
+{
+    using namespace lldb;
+    const uint32_t event_type = event.GetType();
+
+    if (event_type & SBProcess::eBroadcastBitSTDOUT)
+    {
+        // The process has stdout available, get it and write it out to the
+        // appropriate place.
+        GetProcessSTDOUT ();
+    }
+    else if (event_type & SBProcess::eBroadcastBitSTDERR)
+    {
+        // The process has stderr available, get it and write it out to the
+        // appropriate place.
+        GetProcessSTDERR ();
+    }
+    else if (event_type & SBProcess::eBroadcastBitStateChanged)
+    {
+        // Drain all stout and stderr so we don't see any output come after
+        // we print our prompts
+        GetProcessSTDOUT ();
+        GetProcessSTDERR ();
+
+        // Something changed in the process;  get the event and report the process's current status and location to
+        // the user.
+        StateType event_state = SBProcess::GetStateFromEvent (event);
+        if (event_state == eStateInvalid)
+            return;
+
+        SBProcess process (SBProcess::GetProcessFromEvent (event));
+        assert (process.IsValid());
+
+        switch (event_state)
+        {
+        case eStateInvalid:
+        case eStateUnloaded:
+        case eStateAttaching:
+        case eStateLaunching:
+        case eStateStepping:
+        case eStateDetached:
+            {
+                char message[1024];
+                int message_len = ::snprintf (message, sizeof(message), "Process %d %s\n", process.GetProcessID(),
+                                              SBDebugger::StateAsCString (event_state));
+                m_io_channel_ap->OutWrite(message, message_len);
+            }
+            break;
+
+        case eStateRunning:
+            // Don't be chatty when we run...
+            break;
+
+        case eStateExited:
+            SBDebugger::HandleCommand("status");
+            m_io_channel_ap->RefreshPrompt();
+            break;
+
+        case eStateStopped:
+        case eStateCrashed:
+        case eStateSuspended:
+            // Make sure the program hasn't been auto-restarted:
+            if (SBProcess::GetRestartedFromEvent (event))
+            {
+                // FIXME: Do we want to report this, or would that just be annoyingly chatty?
+                char message[1024];
+                int message_len = ::snprintf (message, sizeof(message), "Process %d stopped and was programmatically restarted.\n",
+                                              process.GetProcessID());
+                m_io_channel_ap->OutWrite(message, message_len);
+            }
+            else
+            {
+                UpdateCurrentThread ();
+                SBDebugger::HandleCommand("status");
+                m_io_channel_ap->RefreshPrompt();
+            }
+            break;
+        }
+    }
+}
+
+//  This function handles events broadcast by the IOChannel (HasInput, UserInterrupt, or ThreadShouldExit).
+
+bool
+Driver::HandleIOEvent (const SBEvent &event)
+{
+    bool quit = false;
+
+    const uint32_t event_type = event.GetType();
+
+    if (event_type & IOChannel::eBroadcastBitHasUserInput)
+    {
+        // We got some input (i.e. a command string) from the user; pass it off to the command interpreter for
+        // handling.
+
+        const char *command_string = SBEvent::GetCStringFromEvent(event);
+        if (command_string == NULL)
+            command_string == "";
+        SBCommandReturnObject result;
+        if (SBDebugger::GetCommandInterpreter().HandleCommand (command_string, result, true) != lldb::eReturnStatusQuit)
+        {
+            m_io_channel_ap->ErrWrite (result.GetError(), result.GetErrorSize());
+            m_io_channel_ap->OutWrite (result.GetOutput(), result.GetOutputSize());
+        }
+        // We are done getting and running our command, we can now clear the
+        // m_waiting_for_command so we can get another one.
+        m_waiting_for_command = false;
+
+        // If our editline input reader is active, it means another input reader
+        // got pushed onto the input reader and caused us to become deactivated.
+        // When the input reader above us gets popped, we will get re-activated
+        // and our prompt will refresh in our callback
+        if (m_editline_reader.IsActive())
+        {
+            ReadyForCommand ();
+        }
+    }
+    else if (event_type & IOChannel::eBroadcastBitUserInterrupt)
+    {
+        // This is here to handle control-c interrupts from the user.  It has not yet really been implemented.
+        // TO BE DONE:  PROPERLY HANDLE CONTROL-C FROM USER
+        //m_io_channel_ap->CancelInput();
+        // Anything else?  Send Interrupt to process?
+    }
+    else if ((event_type & IOChannel::eBroadcastBitThreadShouldExit) ||
+             (event_type & IOChannel::eBroadcastBitThreadDidExit))
+    {
+        // If the IOChannel thread is trying to go away, then it is definitely
+        // time to end the debugging session.
+        quit = true;
+    }
+
+    return quit;
+}
+
+
+//struct CrashImageInfo
+//{
+//    std::string path;
+//    VMRange text_range;
+//    UUID uuid;
+//};
+//
+//void
+//Driver::ParseCrashLog (const char *crash_log)
+//{
+//    printf("Parsing crash log: %s\n", crash_log);
+//
+//    char image_path[PATH_MAX];
+//    std::vector<CrashImageInfo> crash_infos;
+//    if (crash_log && crash_log[0])
+//    {
+//        FileSpec crash_log_file (crash_log);
+//        STLStringArray crash_log_lines;
+//        if (crash_log_file.ReadFileLines (crash_log_lines))
+//        {
+//            const size_t num_crash_log_lines = crash_log_lines.size();
+//            size_t i;
+//            for (i=0; i<num_crash_log_lines; ++i)
+//            {
+//                const char *line = crash_log_lines[i].c_str();
+//                if (strstr (line, "Code Type:"))
+//                {
+//                    char arch_string[256];
+//                    if (sscanf(line, "%s", arch_string))
+//                    {
+//                        if (strcmp(arch_string, "X86-64"))
+//                            lldb::GetDefaultArchitecture ().SetArch ("x86_64");
+//                        else if (strcmp(arch_string, "X86"))
+//                            lldb::GetDefaultArchitecture ().SetArch ("i386");
+//                        else
+//                        {
+//                            ArchSpec arch(arch_string);
+//                            if (arch.IsValid ())
+//                                lldb::GetDefaultArchitecture () = arch;
+//                            else
+//                                fprintf(stderr, "Unrecognized architecture: %s\n", arch_string);
+//                        }
+//                    }
+//                }
+//                else
+//                if (strstr(line, "Path:"))
+//                {
+//                    const char *p = line + strlen("Path:");
+//                    while (isspace(*p))
+//                        ++p;
+//
+//                    m_option_data.m_filename.assign (p);
+//                }
+//                else
+//                if (strstr(line, "Binary Images:"))
+//                {
+//                    while (++i < num_crash_log_lines)
+//                    {
+//                        if (crash_log_lines[i].empty())
+//                            break;
+//
+//                        line = crash_log_lines[i].c_str();
+//                        uint64_t text_start_addr;
+//                        uint64_t text_end_addr;
+//                        char uuid_cstr[64];
+//                        int bytes_consumed_before_uuid = 0;
+//                        int bytes_consumed_after_uuid = 0;
+//
+//                        int items_parsed = ::sscanf (line,
+//                                                     "%llx - %llx %*s %*s %*s %n%s %n",
+//                                                     &text_start_addr,
+//                                                     &text_end_addr,
+//                                                     &bytes_consumed_before_uuid,
+//                                                     uuid_cstr,
+//                                                     &bytes_consumed_after_uuid);
+//
+//                        if (items_parsed == 3)
+//                        {
+//
+//                            CrashImageInfo info;
+//                            info.text_range.SetBaseAddress(text_start_addr);
+//                            info.text_range.SetEndAddress(text_end_addr);
+//
+//                            if (uuid_cstr[0] == '<')
+//                            {
+//                                if (info.uuid.SetfromCString (&uuid_cstr[1]) == 0)
+//                                    info.uuid.Clear();
+//
+//                                ::strncpy (image_path, line + bytes_consumed_after_uuid, sizeof(image_path));
+//                            }
+//                            else
+//                            {
+//                                ::strncpy (image_path, line + bytes_consumed_before_uuid, sizeof(image_path));
+//                            }
+//
+//                            info.path = image_path;
+//
+//                            crash_infos.push_back (info);
+//
+//                            info.uuid.GetAsCString(uuid_cstr, sizeof(uuid_cstr));
+//
+//                            printf("0x%16.16llx - 0x%16.16llx <%s> %s\n",
+//                                   text_start_addr,
+//                                   text_end_addr,
+//                                   uuid_cstr,
+//                                   image_path);
+//                        }
+//                    }
+//                }
+//            }
+//        }
+//
+//        if (crash_infos.size())
+//        {
+//            SBTarget target (SBDebugger::CreateTarget (crash_infos.front().path.c_str(),
+//                                                      lldb::GetDefaultArchitecture().AsCString (),
+//                                                      false));
+//            if (target.IsValid())
+//            {
+//
+//            }
+//        }
+//    }
+//}
+//
+
+void
+Driver::MasterThreadBytesReceived (void *baton, const void *src, size_t src_len)
+{
+    Driver *driver = (Driver*)baton;
+    driver->GetFromMaster ((const char *)src, src_len);
+}
+
+void
+Driver::GetFromMaster (const char *src, size_t src_len)
+{
+    // Echo the characters back to the Debugger's stdout, that way if you
+    // type characters while a command is running, you'll see what you've typed.
+    FILE *out_fh = SBDebugger::GetOutputFileHandle();
+    if (out_fh)
+        ::fwrite (src, 1, src_len, out_fh);
+}
+
+size_t
+Driver::EditLineInputReaderCallback 
+(
+    void *baton, 
+    SBInputReader *reader, 
+    InputReaderAction notification,
+    const char *bytes, 
+    size_t bytes_len
+)
+{
+    Driver *driver = (Driver *)baton;
+
+    switch (notification)
+    {
+    case eInputReaderActivate:
+        break;
+
+    case eInputReaderReactivate:
+        driver->ReadyForCommand();
+        break;
+
+    case eInputReaderDeactivate:
+        break;
+
+    case eInputReaderGotToken:
+        write (driver->m_editline_pty.GetMasterFileDescriptor(), bytes, bytes_len);
+        break;
+        
+    case eInputReaderDone:
+        break;
+    }
+    return bytes_len;
+}
+
+void
+Driver::MainLoop ()
+{
+    char error_str[1024];
+    if (m_editline_pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, error_str, sizeof(error_str)) == false)
+    {
+        ::fprintf (stderr, "error: failed to open driver pseudo terminal : %s", error_str);
+        exit(1);
+    }
+    else
+    {
+        const char *driver_slave_name = m_editline_pty.GetSlaveName (error_str, sizeof(error_str));
+        if (driver_slave_name == NULL)
+        {
+            ::fprintf (stderr, "error: failed to get slave name for driver pseudo terminal : %s", error_str);
+            exit(2);
+        }
+        else
+        {
+            m_editline_slave_fh = ::fopen (driver_slave_name, "r+");
+            if (m_editline_slave_fh == NULL)
+            {
+                SBError error;
+                error.SetErrorToErrno();
+                ::fprintf (stderr, "error: failed to get open slave for driver pseudo terminal : %s",
+                           error.GetCString());
+                exit(3);
+            }
+
+            ::setbuf (m_editline_slave_fh, NULL);
+        }
+    }
+
+
+   // struct termios stdin_termios;
+
+    if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0)
+        atexit (reset_stdin_termios);
+
+    ::setbuf (stdin, NULL);
+    ::setbuf (stdout, NULL);
+
+    SBDebugger::SetErrorFileHandle (stderr, false);
+    SBDebugger::SetOutputFileHandle (stdout, false);
+    SBDebugger::SetInputFileHandle (stdin, true);
+
+    // You have to drain anything that comes to the master side of the PTY.  master_out_comm is
+    // for that purpose.  The reason you need to do this is a curious reason...  editline will echo
+    // characters to the PTY when it gets characters while el_gets is not running, and then when
+    // you call el_gets (or el_getc) it will try to reset the terminal back to raw mode which blocks
+    // if there are unconsumed characters in the out buffer.
+    // However, you don't need to do anything with the characters, since editline will dump these
+    // unconsumed characters after printing the prompt again in el_gets.
+
+    SBCommunication master_out_comm("driver.editline");
+    master_out_comm.AdoptFileDesriptor(m_editline_pty.GetMasterFileDescriptor(), false);
+    master_out_comm.SetReadThreadBytesReceivedCallback(Driver::MasterThreadBytesReceived, this);
+
+    if (master_out_comm.ReadThreadStart () == false)
+    {
+        ::fprintf (stderr, "error: failed to start master out read thread");
+        exit(5);
+    }
+
+//    const char *crash_log = GetCrashLogFilename();
+//    if (crash_log)
+//    {
+//        ParseCrashLog (crash_log);
+//    }
+//
+    SBCommandInterpreter sb_interpreter = SBDebugger::GetCommandInterpreter();
+
+    m_io_channel_ap.reset (new IOChannel(m_editline_slave_fh, stdout, stderr, this));
+
+    struct winsize window_size;
+    if (isatty (STDIN_FILENO)
+        && ::ioctl (STDIN_FILENO, TIOCGWINSZ, &window_size) == 0)
+    {
+        char buffer[25];
+
+        sprintf (buffer, "set term-width %d", window_size.ws_col);
+        SBDebugger::HandleCommand ((const char *) buffer);
+    }
+
+    // Since input can be redirected by the debugger, we must insert our editline
+    // input reader in the queue so we know when our reader should be active
+    // and so we can receive bytes only when we are supposed to.
+    SBError err (m_editline_reader.Initialize (Driver::EditLineInputReaderCallback, // callback
+                                               this,                              // baton
+                                               eInputReaderGranularityByte,       // token_size
+                                               NULL,                              // end token - NULL means never done
+                                               NULL,                              // prompt - taken care of elsewhere
+                                               false));                           // echo input - don't need Debugger 
+                                                                                  // to do this, we handle it elsewhere
+    
+    if (err.Fail())
+    {
+        ::fprintf (stderr, "error: %s", err.GetCString());
+        exit (6);
+    }
+    
+    SBDebugger::PushInputReader (m_editline_reader);
+
+    SBListener listener(SBDebugger::GetListener());
+    if (listener.IsValid())
+    {
+
+        listener.StartListeningForEvents (*m_io_channel_ap,
+                                          IOChannel::eBroadcastBitHasUserInput |
+                                          IOChannel::eBroadcastBitUserInterrupt |
+                                          IOChannel::eBroadcastBitThreadShouldExit |
+                                          IOChannel::eBroadcastBitThreadDidStart |
+                                          IOChannel::eBroadcastBitThreadDidExit);
+
+        if (m_io_channel_ap->Start ())
+        {
+            bool iochannel_thread_exited = false;
+
+            listener.StartListeningForEvents (sb_interpreter.GetBroadcaster(),
+                                              SBCommandInterpreter::eBroadcastBitQuitCommandReceived);
+
+            // Before we handle any options from the command line, we parse the
+            // .lldbinit file in the user's home directory.
+            SBCommandReturnObject result;
+            sb_interpreter.SourceInitFileInHomeDirectory(result);
+            if (GetDebugMode())
+            {
+                result.PutError (SBDebugger::GetErrorFileHandle());
+                result.PutOutput (SBDebugger::GetOutputFileHandle());
+            }
+
+            // Now we handle options we got from the command line
+            char command_string[PATH_MAX * 2];
+            const size_t num_source_command_files = GetNumSourceCommandFiles();
+            if (num_source_command_files > 0)
+            {
+                for (size_t i=0; i < num_source_command_files; ++i)
+                {
+                    const char *command_file = GetSourceCommandFileAtIndex(i);
+                    ::snprintf (command_string, sizeof(command_string), "source '%s'", command_file);
+                    SBDebugger::GetCommandInterpreter().HandleCommand (command_string, result, false);
+                    if (GetDebugMode())
+                    {
+                        result.PutError (SBDebugger::GetErrorFileHandle());
+                        result.PutOutput (SBDebugger::GetOutputFileHandle());
+                    }
+                }
+            }
+
+            if (!m_option_data.m_filename.empty())
+            {
+                char arch_name[64];
+                if (SBDebugger::GetDefaultArchitecture (arch_name, sizeof (arch_name)))
+                    ::snprintf (command_string, sizeof (command_string), "file --arch=%s '%s'", arch_name,
+                                m_option_data.m_filename.c_str());
+                else
+                    ::snprintf (command_string, sizeof(command_string), "file '%s'", m_option_data.m_filename.c_str());
+
+                SBDebugger::HandleCommand (command_string);
+            }
+
+            // Now that all option parsing is done, we try and parse the .lldbinit
+            // file in the current working directory
+            sb_interpreter.SourceInitFileInCurrentWorkingDirectory (result);
+            if (GetDebugMode())
+            {
+                result.PutError(SBDebugger::GetErrorFileHandle());
+                result.PutOutput(SBDebugger::GetOutputFileHandle());
+            }
+
+            SBEvent event;
+
+            // Make sure the IO channel is started up before we try to tell it we
+            // are ready for input
+            listener.WaitForEventForBroadcasterWithType (UINT32_MAX, 
+                                                         *m_io_channel_ap,
+                                                         IOChannel::eBroadcastBitThreadDidStart, 
+                                                         event);
+            
+            ReadyForCommand ();
+
+            bool done = false;
+            while (!done)
+            {
+                listener.WaitForEvent (UINT32_MAX, event);
+                if (event.IsValid())
+                {
+                    if (event.GetBroadcaster().IsValid())
+                    {
+                        uint32_t event_type = event.GetType();
+                        if (event.BroadcasterMatchesRef (*m_io_channel_ap))
+                        {
+                            if ((event_type & IOChannel::eBroadcastBitThreadShouldExit) ||
+                                (event_type & IOChannel::eBroadcastBitThreadDidExit))
+                            {
+                                done = true;
+                                if (event_type & IOChannel::eBroadcastBitThreadDidExit)
+                                    iochannel_thread_exited = true;
+                                break;
+                            }
+                            else
+                                done = HandleIOEvent (event);
+                        }
+                        else if (event.BroadcasterMatchesRef (SBDebugger::GetCurrentTarget().GetProcess().GetBroadcaster()))
+                        {
+                            HandleProcessEvent (event);
+                        }
+                        else if (event.BroadcasterMatchesRef (sb_interpreter.GetBroadcaster()))
+                        {
+                            if (event_type & SBCommandInterpreter::eBroadcastBitQuitCommandReceived)
+                                done = true;
+                        }
+                    }
+                }
+            }
+
+            reset_stdin_termios ();
+
+            CloseIOChannelFile ();
+
+            if (!iochannel_thread_exited)
+            {
+                SBEvent event;
+                listener.GetNextEventForBroadcasterWithType (*m_io_channel_ap,
+                                                             IOChannel::eBroadcastBitThreadDidExit,
+                                                             event);
+                if (!event.IsValid())
+                {
+                    // Send end EOF to the driver file descriptor
+                    m_io_channel_ap->Stop();
+                }
+            }
+
+            SBProcess process = SBDebugger::GetCurrentTarget().GetProcess();
+            if (process.IsValid())
+                process.Destroy();
+        }
+    }
+}
+
+
+void
+Driver::ReadyForCommand ()
+{
+    if (m_waiting_for_command == false)
+    {
+        m_waiting_for_command = true;
+        BroadcastEventByType (Driver::eBroadcastBitReadyForInput, true);
+    }
+}
+
+
+int
+main (int argc, char const *argv[])
+{
+
+    SBDebugger::Initialize();
+    
+    SBHostOS::ThreadCreated ("[main]");
+
+    // Do a little setup on the debugger before we get going
+    SBDebugger::SetAsync(true);
+    Driver driver;
+
+    bool valid_args = driver.ParseArgs (argc, argv, stdout, stderr);
+    if (valid_args)
+    {
+        driver.MainLoop ();
+    }
+
+    SBDebugger::Terminate();
+    return 0;
+}